From a0095fde8153a28523b1fbf5c65ef030d25fee20 Mon Sep 17 00:00:00 2001 From: Adam Coldrick Date: Thu, 14 Jan 2016 15:03:57 +0000 Subject: [PATCH] Add more detail to event messages Pass the Referer header (which should be the URL to the webclient which sent the request) and the query string (if applicable) to event messages. This will allow linking in emails, and also make tag email templates simpler. Change-Id: I43c01bb8e8555205b26e86579114d52ef4068397 --- storyboard/db/api/timeline_events.py | 2 ++ storyboard/notifications/notification_hook.py | 2 ++ storyboard/notifications/publisher.py | 11 +++++-- storyboard/notifications/subscriber.py | 2 ++ storyboard/plugin/email/workers.py | 30 ++++++++++++------- storyboard/plugin/event_worker.py | 12 +++++--- storyboard/plugin/subscription/base.py | 6 ++-- .../notifications/test_notification_hook.py | 4 +++ storyboard/tests/plugin/email/test_workers.py | 4 +++ storyboard/tests/plugin/test_event_worker.py | 4 +-- 10 files changed, 56 insertions(+), 21 deletions(-) diff --git a/storyboard/db/api/timeline_events.py b/storyboard/db/api/timeline_events.py index 7967f94f..45ee6f0f 100644 --- a/storyboard/db/api/timeline_events.py +++ b/storyboard/db/api/timeline_events.py @@ -59,7 +59,9 @@ def event_create(values): publish(author_id=request.current_user_id or None, method="POST", + url=request.headers.get('Referer') or None, path=request.path or None, + query_string=request.query_string or None, status=response.status_code or None, resource="timeline_event", resource_id=new_event.id or None, diff --git a/storyboard/notifications/notification_hook.py b/storyboard/notifications/notification_hook.py index fb7016ed..7aa5267a 100644 --- a/storyboard/notifications/notification_hook.py +++ b/storyboard/notifications/notification_hook.py @@ -102,7 +102,9 @@ class NotificationHook(hooks.PecanHook): # happening. publish(author_id=request.current_user_id, method=request.method, + url=request.headers.get('Referer'), path=request.path, + query_string=request.query_string, status=response.status_code, resource=resource, resource_id=resource_id, diff --git a/storyboard/notifications/publisher.py b/storyboard/notifications/publisher.py index 80580e1d..d09c411b 100644 --- a/storyboard/notifications/publisher.py +++ b/storyboard/notifications/publisher.py @@ -131,16 +131,19 @@ class Payload(object): self.payload = payload -def publish(resource, author_id=None, method=None, path=None, status=None, - resource_id=None, sub_resource=None, sub_resource_id=None, - resource_before=None, resource_after=None): +def publish(resource, author_id=None, method=None, url=None, path=None, + query_string=None, status=None, resource_id=None, + sub_resource=None, sub_resource_id=None, resource_before=None, + resource_after=None): """Send a message for an API event to the storyboard exchange. The message will be automatically JSON encoded. :param resource: The extrapolated resource type (project, story, etc). :param author_id: The ID of the author who performed this action. :param method: The HTTP Method used. + :param url: The Referer header from the request. :param path: The HTTP Path used. + :param query_string: The HTTP query string used. :param status: The HTTP Status code of the response. :param resource_id: The ID of the resource. :param sub_resource: The extracted subresource (user_token, etc) @@ -158,7 +161,9 @@ def publish(resource, author_id=None, method=None, path=None, status=None, payload = { "author_id": author_id, "method": method, + "url": url, "path": path, + "query_string": query_string, "status": status, "resource": resource, "resource_id": resource_id, diff --git a/storyboard/notifications/subscriber.py b/storyboard/notifications/subscriber.py index 29b7c87e..dc0ca645 100644 --- a/storyboard/notifications/subscriber.py +++ b/storyboard/notifications/subscriber.py @@ -74,7 +74,9 @@ def handle_event(ext, body): payload = json.loads(body) return ext.obj.event(author_id=payload['author_id'] or None, method=payload['method'] or None, + url=payload['url'] or None, path=payload['path'] or None, + query_string=payload['query_string'] or None, status=payload['status'] or None, resource=payload['resource'] or None, resource_id=payload['resource_id'] or None, diff --git a/storyboard/plugin/email/workers.py b/storyboard/plugin/email/workers.py index 17760168..0b08fabc 100644 --- a/storyboard/plugin/email/workers.py +++ b/storyboard/plugin/email/workers.py @@ -47,15 +47,17 @@ class EmailWorkerBase(EmailPluginBase, WorkerTaskBase): __metaclass__ = abc.ABCMeta - def handle(self, session, author, method, path, status, resource, - resource_id, sub_resource=None, sub_resource_id=None, + def handle(self, session, author, method, url, path, query_string, status, + resource, resource_id, sub_resource=None, sub_resource_id=None, resource_before=None, resource_after=None): """Handle an event. :param session: An event-specific SQLAlchemy session. :param author: The author's user record. :param method: The HTTP Method. + :param url: The Referer header from the request. :param path: The full HTTP Path requested. + :param query_string: The HTTP query string from the request. :param status: The returned HTTP Status of the response. :param resource: The resource type. :param resource_id: The ID of the resource. @@ -85,8 +87,10 @@ class EmailWorkerBase(EmailPluginBase, WorkerTaskBase): author=author, subscribers=subscribers, method=method, + url=url, status=status, path=path, + query_string=query_string, resource=resource, resource_id=resource_id, sub_resource=sub_resource, @@ -95,17 +99,19 @@ class EmailWorkerBase(EmailPluginBase, WorkerTaskBase): resource_after=resource_after) @abc.abstractmethod - def handle_email(self, session, author, subscribers, method, path, status, - resource, resource_id, sub_resource=None, - sub_resource_id=None, resource_before=None, - resource_after=None): + def handle_email(self, session, author, subscribers, method, url, path, + query_string, status, resource, resource_id, + sub_resource=None, sub_resource_id=None, + resource_before=None, resource_after=None): """Handle an email notification for the given subscribers. :param session: An event-specific SQLAlchemy session. :param author: The author's user record. :param subscribers: A list of subscribers that should receive an email. :param method: The HTTP Method. + :param url: The Referer header from the request. :param path: The full HTTP Path requested. + :param query_string: The query string from the request. :param status: The returned HTTP Status of the response. :param resource: The resource type. :param resource_id: The ID of the resource. @@ -189,10 +195,10 @@ class SubscriptionEmailWorker(EmailWorkerBase): have indicated that they wish to receive emails, but don't want digests. """ - def handle_email(self, session, author, subscribers, method, path, status, - resource, resource_id, sub_resource=None, - sub_resource_id=None, resource_before=None, - resource_after=None): + def handle_email(self, session, author, subscribers, method, url, path, + query_string, status, resource, resource_id, + sub_resource=None, sub_resource_id=None, + resource_before=None, resource_after=None): """Send an email for a specific event. We assume that filtering logic has already occurred when this method @@ -202,7 +208,9 @@ class SubscriptionEmailWorker(EmailWorkerBase): :param author: The author's user record. :param subscribers: A list of subscribers that should receive an email. :param method: The HTTP Method. + :param url: The Referer header from the request. :param path: The full HTTP Path requested. + :param query_string: The query string from the request. :param status: The returned HTTP Status of the response. :param resource: The resource type. :param resource_id: The ID of the resource. @@ -269,6 +277,8 @@ class SubscriptionEmailWorker(EmailWorkerBase): author=author, resource=resource_instance, sub_resource=sub_resource_instance, + url=url, + query_string=query_string, before=before, after=after) # Send the email. diff --git a/storyboard/plugin/event_worker.py b/storyboard/plugin/event_worker.py index 295daeb4..e24d2086 100644 --- a/storyboard/plugin/event_worker.py +++ b/storyboard/plugin/event_worker.py @@ -161,8 +161,8 @@ class WorkerTaskBase(PluginBase): __metaclass__ = abc.ABCMeta - def event(self, author_id, method, path, status, resource, resource_id, - sub_resource=None, sub_resource_id=None, + def event(self, author_id, method, url, path, query_string, status, + resource, resource_id, sub_resource=None, sub_resource_id=None, resource_before=None, resource_after=None): """Handle an event. @@ -176,7 +176,9 @@ class WorkerTaskBase(PluginBase): self.handle(session=session, author=author, method=method, + url=url, path=path, + query_string=query_string, status=status, resource=resource, resource_id=resource_id, @@ -193,15 +195,17 @@ class WorkerTaskBase(PluginBase): return db_api.entity_get(klass, resource_id, session=session) @abc.abstractmethod - def handle(self, session, author, method, path, status, resource, - resource_id, sub_resource=None, sub_resource_id=None, + def handle(self, session, author, method, url, path, query_string, status, + resource, resource_id, sub_resource=None, sub_resource_id=None, resource_before=None, resource_after=None): """Handle an event. :param session: An event-specific SQLAlchemy session. :param author: The author's user record. :param method: The HTTP Method. + :param url: The Referer header from the request. :param path: The full HTTP Path requested. + :param query_string: The HTTP query string provided. :param status: The returned HTTP Status of the response. :param resource: The resource type. :param resource_id: The ID of the resource. diff --git a/storyboard/plugin/subscription/base.py b/storyboard/plugin/subscription/base.py index c11932ba..ee4042d4 100644 --- a/storyboard/plugin/subscription/base.py +++ b/storyboard/plugin/subscription/base.py @@ -28,8 +28,8 @@ class Subscription(WorkerTaskBase): """ return True - def handle(self, session, author, method, path, status, resource, - resource_id, sub_resource=None, sub_resource_id=None, + def handle(self, session, author, method, url, path, query_string, status, + resource, resource_id, sub_resource=None, sub_resource_id=None, resource_before=None, resource_after=None): """This worker handles API events and attempts to determine whether they correspond to user subscriptions. @@ -37,7 +37,9 @@ class Subscription(WorkerTaskBase): :param session: An event-specific SQLAlchemy session. :param author: The author's user record. :param method: The HTTP Method. + :param url: The Referer header from the request. :param path: The full HTTP Path requested. + :param query_string: The HTTP query string from the request. :param status: The returned HTTP Status of the response. :param resource: The resource type. :param resource_id: The ID of the resource. diff --git a/storyboard/tests/notifications/test_notification_hook.py b/storyboard/tests/notifications/test_notification_hook.py index 82a4d1dc..981c5a1c 100644 --- a/storyboard/tests/notifications/test_notification_hook.py +++ b/storyboard/tests/notifications/test_notification_hook.py @@ -252,6 +252,8 @@ class TestNotificationHook(base.BaseDbTestCase): mock_state = Mock() mock_state.request.current_user_id = '1' mock_state.request.method = 'PUT' + mock_state.request.headers = {'Referer': 'http://localhost/'} + mock_state.request.query_string = '' mock_state.request.path = '/v1/tasks/1' mock_state.response.status_code = '200' mock_state.old_entity_values = sot_json @@ -261,7 +263,9 @@ class TestNotificationHook(base.BaseDbTestCase): mock_publish.assert_called_with( author_id=mock_state.request.current_user_id, method=mock_state.request.method, + url=mock_state.request.headers['Referer'], path=mock_state.request.path, + query_string=mock_state.request.query_string, status=mock_state.response.status_code, resource='task', resource_id='1', diff --git a/storyboard/tests/plugin/email/test_workers.py b/storyboard/tests/plugin/email/test_workers.py index f8e6fbcc..6fe74985 100644 --- a/storyboard/tests/plugin/email/test_workers.py +++ b/storyboard/tests/plugin/email/test_workers.py @@ -42,7 +42,9 @@ class TestEmailWorkerBase(base.FunctionalTest): worker_base.handle(session=session, author=user_1, method='POST', + url='http://localhost/', path='/test', + query_string='', status=201, resource='story', resource_id=1) @@ -154,7 +156,9 @@ class TestSubscriptionEmailWorker(base.FunctionalTest): author=author, subscribers=subscribers, method='PUT', + url='http://localhost/', path='/stories/1', + query_string='', status=200, resource='story', resource_id=1, diff --git a/storyboard/tests/plugin/test_event_worker.py b/storyboard/tests/plugin/test_event_worker.py index a183852f..f72b5d13 100644 --- a/storyboard/tests/plugin/test_event_worker.py +++ b/storyboard/tests/plugin/test_event_worker.py @@ -65,8 +65,8 @@ class TestWorkerTaskBase(base.FunctionalTest): class TestWorkerPlugin(plugin_base.WorkerTaskBase): - def handle(self, session, author, method, path, status, resource, - resource_id, sub_resource=None, sub_resource_id=None, + def handle(self, session, author, method, url, path, query_string, status, + resource, resource_id, sub_resource=None, sub_resource_id=None, resource_before=None, resource_after=None): pass