-
Notifications
You must be signed in to change notification settings - Fork 20
Application Multiplexer #844
Changes from 1 commit
777e4b3
6a5379f
8c97624
eadd57d
e920803
8ada227
4ba07ed
4430cf8
f4784c6
cce9f7f
a9b1875
cdb71d1
dfa3e67
c2ab39d
26ca348
370dd5d
e4cf441
5000b2d
a68f96c
3f2656a
e289419
1eb8a7b
581c142
7dfe830
b5cee25
e000b41
0c52ebf
eaa947d
9db74c7
52cceec
0be9e4c
db58f15
97c9bb6
6577351
216f447
eafccbe
b2be461
b894b25
0780889
aeceb82
8331801
7a81223
dd6d269
280404e
22e2192
29c7306
4ce79e6
b5f9dbf
7fe7f3e
45f53b4
e3b47bf
3727f56
a2d2a00
f19ab98
aa55b10
098b6cc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from go.vumitools.router.definition import RouterDefinitionBase | ||
|
||
|
||
class RouterDefinition(RouterDefinitionBase): | ||
router_type = 'application_multiplexer' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will need to be updated when we update the namespace name. |
||
|
||
def configured_outbound_endpoints(self, config): | ||
return list(set(config.get('keyword_endpoint_mapping', {}).values())) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
from go.base.tests.helpers import GoDjangoTestCase | ||
from go.routers.tests.view_helpers import RouterViewsHelper | ||
|
||
|
||
class ApplicationMultiplexerViewTests(GoDjangoTestCase): | ||
|
||
def setUp(self): | ||
self.router_helper = self.add_helper( | ||
RouterViewsHelper(u'application_multiplexer') | ||
) | ||
self.user_helper = self.router_helper.vumi_helper.get_or_create_user() | ||
self.client = self.router_helper.get_client() | ||
|
||
def test_new_router(self): | ||
router_store = self.user_helper.user_api.router_store | ||
self.assertEqual([], router_store.list_routers()) | ||
|
||
response = self.client.post(self.router_helper.get_new_view_url(), { | ||
'name': u"myrouter", | ||
'router_type': u'application_multiplexer', | ||
}) | ||
[router_key] = router_store.list_routers() | ||
rtr_helper = self.router_helper.get_router_helper_by_key(router_key) | ||
self.assertRedirects(response, rtr_helper.get_view_url('edit')) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
from twisted.internet.defer import inlineCallbacks | ||
|
||
from vumi.tests.helpers import VumiTestCase | ||
|
||
from go.routers.app_multiplexer.vumi_app import ApplicationMultiplexer | ||
from go.routers.tests.helpers import RouterWorkerHelper | ||
|
||
|
||
class TestApplicationMultiplexerRouter(VumiTestCase): | ||
|
||
router_class = ApplicationMultiplexer | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This attribute is unnecessary and should be removed. |
||
|
||
@inlineCallbacks | ||
def setUp(self): | ||
self.router_helper = self.add_helper( | ||
RouterWorkerHelper(ApplicationMultiplexer) | ||
) | ||
self.router_worker = yield self.router_helper.get_router_worker({}) | ||
|
||
@inlineCallbacks | ||
def assert_routed_inbound(self, content, router, expected_endpoint): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nothing calls this. Can it be removed? |
||
msg = yield self.router_helper.ri.make_dispatch_inbound( | ||
content, router=router) | ||
emsg = msg.copy() | ||
emsg.set_routing_endpoint(expected_endpoint) | ||
rmsg = self.router_helper.ro.get_dispatched_inbound()[-1] | ||
self.assertEqual(emsg, rmsg) | ||
|
||
@inlineCallbacks | ||
def test_start(self): | ||
router = yield self.router_helper.create_router() | ||
self.assertTrue(router.stopped()) | ||
self.assertFalse(router.running()) | ||
|
||
yield self.router_helper.start_router(router) | ||
router = yield self.router_helper.get_router(router.key) | ||
self.assertFalse(router.stopped()) | ||
self.assertTrue(router.running()) | ||
|
||
@inlineCallbacks | ||
def test_stop(self): | ||
router = yield self.router_helper.create_router(started=True) | ||
self.assertFalse(router.stopped()) | ||
self.assertTrue(router.running()) | ||
|
||
yield self.router_helper.stop_router(router) | ||
router = yield self.router_helper.get_router(router.key) | ||
self.assertTrue(router.stopped()) | ||
self.assertFalse(router.running()) | ||
|
||
@inlineCallbacks | ||
def test_no_messages_processed_while_stopped(self): | ||
router = yield self.router_helper.create_router() | ||
|
||
yield self.router_helper.ri.make_dispatch_inbound("foo", router=router) | ||
self.assertEqual([], self.router_helper.ro.get_dispatched_inbound()) | ||
|
||
yield self.router_helper.ri.make_dispatch_ack(router=router) | ||
self.assertEqual([], self.router_helper.ro.get_dispatched_events()) | ||
|
||
yield self.router_helper.ro.make_dispatch_outbound( | ||
"foo", router=router) | ||
self.assertEqual([], self.router_helper.ri.get_dispatched_outbound()) | ||
[nack] = self.router_helper.ro.get_dispatched_events() | ||
self.assertEqual(nack['event_type'], 'nack') | ||
|
||
@inlineCallbacks | ||
def test_inbound_no_config(self): | ||
router = yield self.router_helper.create_router(started=True) | ||
yield self.assert_routed_inbound("foo bar", router, 'default') | ||
yield self.assert_routed_inbound("baz quux", router, 'default') |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
from django import forms | ||
|
||
from go.router.view_definition import RouterViewDefinitionBase, EditRouterView | ||
|
||
|
||
class ApplicationMultiplexerForm(forms.Form): | ||
keyword = forms.CharField() | ||
target_endpoint = forms.CharField() | ||
|
||
|
||
class BaseApplicationMultiplexerFormSet(forms.formsets.BaseFormSet): | ||
@staticmethod | ||
def initial_from_config(data): | ||
return [{'keyword': k, 'target_endpoint': v} | ||
for k, v in sorted(data.items())] | ||
|
||
def to_config(self): | ||
keyword_endpoint_mapping = {} | ||
for form in self: | ||
if (not form.is_valid()) or form.cleaned_data['DELETE']: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Elsewhere we use |
||
continue | ||
keyword = form.cleaned_data['keyword'] | ||
target_endpoint = form.cleaned_data['target_endpoint'] | ||
keyword_endpoint_mapping[keyword] = target_endpoint | ||
return keyword_endpoint_mapping | ||
|
||
|
||
ApplicationMultiplexerFormSet = forms.formsets.formset_factory( | ||
ApplicationMultiplexerForm, | ||
can_delete=True, | ||
extra=1, | ||
formset=BaseApplicationMultiplexerFormSet) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest putting these forms in a |
||
|
||
|
||
class EditApplicationMultiplexerView(EditRouterView): | ||
edit_forms = ( | ||
('keyword_endpoint_mapping', ApplicationMultiplexerFormSet), | ||
) | ||
|
||
|
||
class RouterViewDefinition(RouterViewDefinitionBase): | ||
edit_view = EditApplicationMultiplexerView |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# -*- test-case-name: go.routers.keyword.tests.test_vumi_app -*- | ||
# -*- coding: utf-8 -*- | ||
|
||
from vumi import log | ||
from vumi.config import ConfigDict | ||
|
||
from go.vumitools.app_worker import GoRouterWorker | ||
|
||
|
||
class ApplicationMultiplexerConfig(GoRouterWorker.CONFIG_CLASS): | ||
keyword_endpoint_mapping = ConfigDict( | ||
"Mapping from case-insensitive keyword regex to endpoint name.", | ||
default={}) | ||
|
||
|
||
class ApplicationMultiplexer(GoRouterWorker): | ||
""" | ||
Router that splits inbound messages based on keywords. | ||
""" | ||
CONFIG_CLASS = ApplicationMultiplexerConfig | ||
|
||
worker_name = 'application_multiplexer' | ||
|
||
def lookup_target(self, config, msg): | ||
first_word = ((msg['content'] or '').strip().split() + [''])[0] | ||
for keyword, target in config.keyword_endpoint_mapping.iteritems(): | ||
if keyword.lower() == first_word.lower(): | ||
return target | ||
return 'default' | ||
|
||
def handle_inbound(self, config, msg, conn_name): | ||
log.debug("Handling inbound: %s" % (msg,)) | ||
return self.publish_inbound(msg, self.lookup_target(config, msg)) | ||
|
||
def handle_outbound(self, config, msg, conn_name): | ||
log.debug("Handling outbound: %s" % (msg,)) | ||
return self.publish_outbound(msg, 'default') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably best if the namespace matches the module name, i.e.
app_multiplexer
. We don't do that in a few places but those are all places which we want to get rid of eventually.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
namespace
value needs to change to match the module name.