Pass the old entity values in the NotificationHook
This change allows in the NotificationHook after method to access the old state of the entity changed by the API call, via the state.old_entity_values variable. We may want to add that to the publish method signature in a later change, so we get in the events queue a message showing the old vs new state of the resource changed. Change-Id: I0f21e512cdd4e2904e6b1f0c2c2aa4070e5ab786
This commit is contained in:
parent
b5bf53e831
commit
1d759aa113
@ -18,9 +18,20 @@ import re
|
||||
|
||||
from pecan import hooks
|
||||
|
||||
from storyboard.api.v1 import wmodels
|
||||
import storyboard.common.hook_priorities as priority
|
||||
from storyboard.db.api import base as api_base
|
||||
from storyboard.db import models
|
||||
from storyboard.notifications.publisher import publish
|
||||
|
||||
class_mappings = {'task': [models.Task, wmodels.Task],
|
||||
'project_group': [models.ProjectGroup, wmodels.ProjectGroup],
|
||||
'project': [models.ProjectGroup, wmodels.Project],
|
||||
'user': [models.User, wmodels.User],
|
||||
'team': [models.Team, wmodels.Team],
|
||||
'story': [models.Story, wmodels.Story],
|
||||
'tag': [models.StoryTag, wmodels.Tag]}
|
||||
|
||||
|
||||
class NotificationHook(hooks.PecanHook):
|
||||
|
||||
@ -29,6 +40,21 @@ class NotificationHook(hooks.PecanHook):
|
||||
def __init__(self):
|
||||
super(NotificationHook, self).__init__()
|
||||
|
||||
def before(self, state):
|
||||
# Ignore get methods, we only care about changes.
|
||||
if state.request.method not in ['POST', 'PUT', 'DELETE']:
|
||||
return
|
||||
|
||||
request = state.request
|
||||
|
||||
# Attempt to determine the type of the payload. This checks for
|
||||
# nested paths.
|
||||
(resource, resource_id, subresource, subresource_id) \
|
||||
= self.parse(request.path)
|
||||
|
||||
state.old_entity_values = self.get_original_resource(resource,
|
||||
resource_id)
|
||||
|
||||
def after(self, state):
|
||||
# Ignore get methods, we only care about changes.
|
||||
if state.request.method not in ['POST', 'PUT', 'DELETE']:
|
||||
@ -41,11 +67,10 @@ class NotificationHook(hooks.PecanHook):
|
||||
# nested paths.
|
||||
(resource, resource_id, subresource, subresource_id) \
|
||||
= self.parse(request.path)
|
||||
if not resource:
|
||||
return
|
||||
|
||||
# On a POST method, the server has assigned an ID to the resource,
|
||||
# so we should be getting it from the resource rather than the URL.
|
||||
if state.request.method == 'POST':
|
||||
# When a resource is created..
|
||||
response_body = json.loads(response.body)
|
||||
if response_body:
|
||||
resource_id = response_body.get('id')
|
||||
@ -64,6 +89,22 @@ class NotificationHook(hooks.PecanHook):
|
||||
sub_resource=subresource,
|
||||
sub_resource_id=subresource_id)
|
||||
|
||||
def get_original_resource(self, resource, resource_id):
|
||||
"""Given a resource name and ID, will load that resource and map it
|
||||
to a JSON object.
|
||||
"""
|
||||
if not resource or not resource_id or resource not in \
|
||||
class_mappings.keys():
|
||||
return None
|
||||
|
||||
model_class, wmodel_class = class_mappings[resource]
|
||||
entity = api_base.entity_get(model_class, resource_id)
|
||||
if entity:
|
||||
return wmodel_class.from_db_model(entity)
|
||||
else:
|
||||
# In the case of a DELETE, the entity will be returned as None
|
||||
return None
|
||||
|
||||
def parse(self, s):
|
||||
url_pattern = re.match("^\/v1\/([a-z_]+)\/?([0-9]+)?"
|
||||
"\/?([a-z]+)?\/?([0-9]+)?$", s)
|
||||
|
@ -12,13 +12,16 @@
|
||||
# implied. See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from mock import patch, Mock
|
||||
from storyboard.api.v1.v1_controller import V1Controller
|
||||
from storyboard.api.v1.wmodels import Task as TaskWmodel
|
||||
import storyboard.common.hook_priorities as priority
|
||||
from storyboard.db.models import Task
|
||||
from storyboard.notifications.notification_hook import NotificationHook
|
||||
import storyboard.tests.base as base
|
||||
|
||||
|
||||
class TestNotificationHook(base.TestCase):
|
||||
class TestNotificationHook(base.DbTestCase):
|
||||
|
||||
def test_priority(self):
|
||||
"""Assert that this hook has default priority."""
|
||||
@ -114,3 +117,104 @@ class TestNotificationHook(base.TestCase):
|
||||
n.singularize_resource('systeminfo'))
|
||||
self.assertEqual('openid',
|
||||
n.singularize_resource('openid'))
|
||||
|
||||
def test_map_resource_invalid_options(self):
|
||||
"""Assert that the map_resource method behaves as expected when
|
||||
receiving invalid values.
|
||||
"""
|
||||
n = NotificationHook()
|
||||
|
||||
# Test no resource
|
||||
self.assertIsNone(n.get_original_resource(None, None))
|
||||
|
||||
# Test invalid resource.
|
||||
self.assertIsNone(n.get_original_resource('invalid', 1))
|
||||
|
||||
# Test no resource_id
|
||||
self.assertIsNone(n.get_original_resource('story', None))
|
||||
|
||||
# Test invalid (gone) resource_id.
|
||||
self.assertIsNone(n.get_original_resource('story', 1000000))
|
||||
|
||||
@patch('storyboard.db.api.base.entity_get')
|
||||
def test_map_resource_valid_options(self, mock_entity_get):
|
||||
"""Assert that the map_resource method behaves as expected when
|
||||
receiving valid values.
|
||||
"""
|
||||
n = NotificationHook()
|
||||
|
||||
# Mock entity_get method to return a sample Task
|
||||
sample_task = Task(id=1,
|
||||
creator_id=1,
|
||||
title='Test',
|
||||
status='inprogress',
|
||||
story_id=1,
|
||||
project_id=1,
|
||||
assignee_id=1,
|
||||
priority='medium')
|
||||
mock_entity_get.return_value = sample_task
|
||||
|
||||
sample_task_wmodel = TaskWmodel.from_db_model(sample_task)
|
||||
old_entity_values = n.get_original_resource('task', 1)
|
||||
|
||||
self.assertEquals(old_entity_values.id,
|
||||
sample_task_wmodel.id)
|
||||
self.assertEquals(old_entity_values.creator_id,
|
||||
sample_task_wmodel.creator_id)
|
||||
self.assertEquals(old_entity_values.title,
|
||||
sample_task_wmodel.title)
|
||||
self.assertEquals(old_entity_values.status,
|
||||
sample_task_wmodel.status)
|
||||
self.assertEquals(old_entity_values.story_id,
|
||||
sample_task_wmodel.story_id)
|
||||
self.assertEquals(old_entity_values.project_id,
|
||||
sample_task_wmodel.project_id)
|
||||
self.assertEquals(old_entity_values.assignee_id,
|
||||
sample_task_wmodel.assignee_id)
|
||||
self.assertEquals(old_entity_values.priority,
|
||||
sample_task_wmodel.priority)
|
||||
|
||||
@patch('storyboard.db.api.base.entity_get')
|
||||
def test_old_entity_values_set_on_state_by_before(self, mock_entity_get):
|
||||
"""Tests that the values of the resource being changed by the
|
||||
request are retrieved in the before method and stored in the
|
||||
state object as variable 'old_returned_values'.
|
||||
"""
|
||||
n = NotificationHook()
|
||||
|
||||
# Mocking state object to simulate a 'PUT' request for task
|
||||
# resource 1
|
||||
mock_state = Mock()
|
||||
mock_state.request.method = 'PUT'
|
||||
mock_state.request.path = '/v1/tasks/1'
|
||||
|
||||
# Mock entity_get method to return a sample Task
|
||||
sample_task = Task(id=1,
|
||||
creator_id=1,
|
||||
title='Test',
|
||||
status='inprogress',
|
||||
story_id=1,
|
||||
project_id=1,
|
||||
assignee_id=1,
|
||||
priority='medium')
|
||||
mock_entity_get.return_value = sample_task
|
||||
|
||||
sample_task_wmodel = TaskWmodel.from_db_model(sample_task)
|
||||
n.before(mock_state)
|
||||
|
||||
self.assertEquals(mock_state.old_entity_values.id,
|
||||
sample_task_wmodel.id)
|
||||
self.assertEquals(mock_state.old_entity_values.creator_id,
|
||||
sample_task_wmodel.creator_id)
|
||||
self.assertEquals(mock_state.old_entity_values.title,
|
||||
sample_task_wmodel.title)
|
||||
self.assertEquals(mock_state.old_entity_values.status,
|
||||
sample_task_wmodel.status)
|
||||
self.assertEquals(mock_state.old_entity_values.story_id,
|
||||
sample_task_wmodel.story_id)
|
||||
self.assertEquals(mock_state.old_entity_values.project_id,
|
||||
sample_task_wmodel.project_id)
|
||||
self.assertEquals(mock_state.old_entity_values.assignee_id,
|
||||
sample_task_wmodel.assignee_id)
|
||||
self.assertEquals(mock_state.old_entity_values.priority,
|
||||
sample_task_wmodel.priority)
|
||||
|
Loading…
x
Reference in New Issue
Block a user