API and engine support for receiver notifying

This patch adds basic support for receiver notifying in
API and engine service layers.

Change-Id: Iddf561a3d94014abd80fabe5f8f482acc9c74661
This commit is contained in:
yanyanhu 2016-09-08 03:48:34 -04:00
parent 654487f86c
commit 7edb329c16
9 changed files with 123 additions and 1 deletions

View File

@ -41,6 +41,7 @@
"receivers:create": "",
"receivers:get": "",
"receivers:delete": "",
"receivers:notify": "",
"actions:index": "",
"actions:get": "",
"events:index": "",

View File

@ -133,3 +133,8 @@ class ReceiverController(wsgi.Controller):
def delete(self, req, receiver_id):
self.rpc_client.receiver_delete(req.context, receiver_id, cast=False)
raise exc.HTTPNoContent()
@util.policy_enforce
def notify(self, req, receiver_id, body=None):
self.rpc_client.receiver_notify(req.context, receiver_id, body)
raise exc.HTTPNoContent()

View File

@ -254,6 +254,10 @@ class API(wsgi.Router):
"/receivers/{receiver_id}",
action="delete",
conditions={'method': 'DELETE'})
sub_mapper.connect("receiver_notify",
"/receivers/{receiver_id}/notify",
action="notify",
conditions={'method': 'POST'})
# Webhooks
res = wsgi.Resource(webhooks.WebhookController(conf))

View File

@ -2197,6 +2197,26 @@ class EngineService(service.Service):
receiver_mod.Receiver.delete(context, db_receiver.id)
LOG.info(_LI("Receiver %s is deleted."), identity)
@request_context
def receiver_notify(self, context, identity, params=None):
"""Handle notification to specified receiver.
:param context: An instance of the request context.
:param identity: The UUID, name or short-id of a receiver.
:param params: Parameters received from notification.
"""
db_receiver = self.receiver_find(context, identity)
# permission checking
if not context.is_admin and context.user != db_receiver.user:
raise exception.Forbidden()
# Receiver type check
if db_receiver.type != consts.RECEIVER_MESSAGE:
raise exception.Forbidden()
# TODO(Yanyanhu): add notification handling logic
LOG.info(_LI("Received notification to receiver %s."), identity)
@request_context
def webhook_trigger(self, context, identity, params=None):

View File

@ -338,6 +338,11 @@ class EngineClient(object):
return rpc_method(ctxt, self.make_msg('receiver_delete',
identity=identity))
def receiver_notify(self, ctxt, identity, params=None):
return self.call(ctxt,
self.make_msg('receiver_notify', identity=identity,
params=params))
def webhook_trigger(self, ctxt, identity, params=None):
return self.call(ctxt,
self.make_msg('webhook_trigger', identity=identity,

View File

@ -533,3 +533,36 @@ class ReceiverControllerTest(shared.ControllerTest, base.SenlinTestCase):
self.assertEqual(403, resp.status_int)
self.assertIn('403 Forbidden', six.text_type(resp))
def test_receiver_notify_success(self, mock_enforce):
self._mock_enforce_setup(mock_enforce, 'notify')
wid = 'aaaa-bbbb-cccc'
req = self._post('/receivers/%(receiver_id)s/notify' % {
'receiver_id': wid}, None)
mock_call = self.patchobject(rpc_client.EngineClient, 'call',
return_value=None)
self.assertRaises(exc.HTTPNoContent,
self.controller.notify, req, receiver_id=wid)
mock_call.assert_called_with(
req.context,
('receiver_notify', {'identity': wid, 'params': None}))
def test_receiver_notify_not_found(self, mock_enforce):
self._mock_enforce_setup(mock_enforce, 'notify')
wid = 'aaaa-bbbb-cccc'
req = self._post('/receivers/%(receiver_id)s/notify' % {
'receiver_id': wid}, None)
error = senlin_exc.ResourceNotFound(type='receiver', id=wid)
mock_call = self.patchobject(rpc_client.EngineClient, 'call')
mock_call.side_effect = shared.to_remote_error(error)
resp = shared.request_with_middleware(fault.FaultWrapper,
self.controller.notify,
req, receiver_id=wid)
self.assertEqual(404, resp.json['code'])
self.assertEqual('ResourceNotFound', resp.json['error']['type'])

View File

@ -399,6 +399,16 @@ class RoutesTest(base.SenlinTestCase):
'receiver_id': 'bbbb'
})
self.assertRoute(
self.m,
'/receivers/bbbb/notify',
'POST',
'notify',
'ReceiverController',
{
'receiver_id': 'bbbb'
})
def test_webhook_collection(self):
self.assertRoute(
self.m,

View File

@ -94,7 +94,7 @@ class ControllerTest(object):
self.context = req.context
ver = version if version else wsgi.DEFAULT_API_VERSION
req.version_request = vr.APIVersionRequest(ver)
req.body = encodeutils.safe_encode(data)
req.body = encodeutils.safe_encode(data) if data else None
return req
def _post(self, path, data, content_type='application/json', version=None):

View File

@ -370,3 +370,47 @@ class ReceiverTest(base.SenlinTestCase):
ex = self.assertRaises(rpc.ExpectedException,
self.eng.receiver_delete, self.ctx, 'Bogus')
self.assertEqual(exc.ResourceNotFound, ex.exc_info[0])
@mock.patch.object(service.EngineService, 'receiver_find')
def test_receiver_notify(self, mock_find):
fake_obj = mock.Mock()
fake_obj.id = 'FAKE_ID'
fake_obj.type = 'message'
fake_obj.user = self.ctx.user
mock_find.return_value = fake_obj
result = self.eng.receiver_notify(self.ctx, 'FAKE_RECEIVER')
self.assertIsNone(result)
mock_find.assert_called_once_with(self.ctx, 'FAKE_RECEIVER')
@mock.patch.object(service.EngineService, 'receiver_find')
def test_receiver_notify_not_found(self, mock_find):
mock_find.side_effect = exc.ResourceNotFound(type='receiver', id='RR')
ex = self.assertRaises(rpc.ExpectedException,
self.eng.receiver_notify, self.ctx, 'Bogus')
self.assertEqual(exc.ResourceNotFound, ex.exc_info[0])
@mock.patch.object(service.EngineService, 'receiver_find')
def test_receiver_notify_permission_check_fail(self, mock_find):
fake_obj = mock.Mock()
fake_obj.id = 'FAKE_ID'
fake_obj.user = 'foo'
mock_find.return_value = fake_obj
ex = self.assertRaises(rpc.ExpectedException,
self.eng.receiver_notify, self.ctx, 'Bogus')
self.assertEqual(exc.Forbidden, ex.exc_info[0])
@mock.patch.object(service.EngineService, 'receiver_find')
def test_receiver_notify_incorrect_type(self, mock_find):
fake_obj = mock.Mock()
fake_obj.id = 'FAKE_ID'
fake_obj.user = self.ctx.user
fake_obj.type = 'not_message'
mock_find.return_value = fake_obj
ex = self.assertRaises(rpc.ExpectedException,
self.eng.receiver_notify, self.ctx, 'Bogus')
self.assertEqual(exc.Forbidden, ex.exc_info[0])