Skip to content

Commit 0b98a29

Browse files
author
Mathieu Mitchell
committed
Create specific routes for every module
This will make Flask return a 404 when required and will solve issue #1
1 parent a9dd94b commit 0b98a29

6 files changed

Lines changed: 56 additions & 30 deletions

File tree

tests/test_api.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,18 @@ def setUp(self):
2626
self.app = Flask('test_app')
2727
self.api_client = self.app.test_client()
2828
self.router = mock.Mock()
29+
self.module1 = mock.Mock()
30+
self.module2 = mock.Mock()
31+
self.modules = {'module1': self.module1,
32+
'module2': self.module2}
2933

30-
self.api = Api(self.app, self.router)
34+
self.api = Api(self.modules, self.app, self.router)
3135

3236
def test_list_implemented_methods(self):
3337
self.router.list_implemented_methods.return_value = ['abcd', 'efgh']
3438

3539
output = self.api_client.get('/module1/')
36-
self.router.list_implemented_methods.assert_called_with('module1')
40+
self.router.list_implemented_methods.assert_called_with(self.module1)
3741

3842
assert_that(json.loads(output.data.decode(output.charset)), is_({
3943
"implemented_methods": [
@@ -57,7 +61,7 @@ def test_execute_method_returns_string(self):
5761
}
5862
))
5963

60-
self.router.invoke_method.assert_called_with(module_name='module2', method='remote_method', params=[], env={'variable1': 'value1'}, callback={})
64+
self.router.invoke_method.assert_called_with(module=self.module2, method='remote_method', params=[], env={'variable1': 'value1'}, callback={})
6165
assert_that(json.loads(output.data.decode(output.charset)), is_('simple string'))
6266

6367
def test_execute_method_returns_list(self):
@@ -75,6 +79,26 @@ def test_execute_method_returns_list(self):
7579
}
7680
))
7781

78-
self.router.invoke_method.assert_called_with(module_name='module2', method='remote_method', params=[], env={'variable1': 'value1'}, callback={})
82+
self.router.invoke_method.assert_called_with(module=self.module2, method='remote_method', params=[], env={'variable1': 'value1'}, callback={})
7983
assert_that(json.loads(output.data.decode(output.charset)), is_(['a', 'b', 'c']))
8084

85+
def test_invoking_unknown_module_returns_a_404(self):
86+
output = self.api_client.post('/new_module/',
87+
headers={'Content-Type': 'application/json'},
88+
data=json.dumps(
89+
{
90+
"method": "remote_method",
91+
"params": [],
92+
"env": {
93+
"variable1": "value1"
94+
},
95+
"callback": {}
96+
}
97+
))
98+
99+
assert_that(output.status_code, is_(404))
100+
101+
def test_listing_unknown_module_returns_a_404(self):
102+
output = self.api_client.get('/new_module/')
103+
104+
assert_that(output.status_code, is_(404))

tests/test_router.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,18 @@ def setUp(self):
4545
'module2': self.module2,
4646
'module3': self.module3}
4747

48-
self.basic_router = Router(self.modules, env_as_kwarg=False)
49-
self.router = Router(self.modules)
48+
self.basic_router = Router(env_as_kwarg=False)
49+
self.router = Router()
5050

5151
def test_basic_mode(self):
52-
result = self.basic_router.invoke_method(module_name='module1', method='my_method_name',
52+
result = self.basic_router.invoke_method(module=self.module1, method='my_method_name',
5353
params=['value1', 'value2'])
5454

5555
self.module1.my_method_name.assert_called_with('value1', 'value2')
5656
assert_that(result, is_('yes'))
5757

5858
def test_env_is_optionally_passed_as_keyword_argument_in_every_call(self):
59-
result = self.router.invoke_method(module_name='module2', method='hello',
59+
result = self.router.invoke_method(module=self.module2, method='hello',
6060
params=['value1', 'value2'],
6161
env={'local_variable1': 'value1', 'local_variable2': 'value2'})
6262

@@ -65,10 +65,10 @@ def test_env_is_optionally_passed_as_keyword_argument_in_every_call(self):
6565
assert_that(result, is_('world'))
6666

6767
def test_list_implemented_methods(self):
68-
result = self.router.list_implemented_methods('module3')
68+
result = self.router.list_implemented_methods(self.module3)
6969

7070
assert_that(result, is_(['ab', 'cd']))
7171

7272
def test_accept_callback_as_kwarg(self):
73-
self.router.invoke_method(module_name='module1', method='hello', params=[], env={},
73+
self.router.invoke_method(module=self.module1, method='hello', params=[], env={},
7474
callback={'url': 'http://example.net', 'params': {'k1': 'v1', 'k2': 'v2'}})

tests/test_server.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ def test_starting_a_server(self, api_mock, router_mock, flask_mock):
3434
s = server.Server(modules)
3535
s.run()
3636

37-
router_mock.assert_called_with(modules)
38-
api_mock.assert_called_with(flask_instance, router_instance)
37+
router_mock.assert_called_with()
38+
api_mock.assert_called_with(modules, flask_instance, router_instance)
3939
flask_instance.run.assert_called_with()
4040

4141
def test_run_passes_parameters_to_flask(self, api_mock, router_mock, flask_mock):

ubersmith_remote_module_server/api.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,35 @@
1414

1515
import json
1616
from flask import request, current_app
17+
import functools
1718

1819

1920
class Api(object):
20-
def __init__(self, app, router):
21+
def __init__(self, modules, app, router):
2122
self.app = app
2223
self.router = router
2324

24-
app.add_url_rule('/<module_name>/', view_func=self.list_implemented_methods, methods=['GET'])
25-
app.add_url_rule('/<module_name>/', view_func=self.handle_remote_invocation, methods=['POST'])
25+
for module_name, module in modules.items():
26+
list_endpoint = functools.partial(self.list_implemented_methods, module)
27+
list_endpoint.__name__ = "list_" + module_name
28+
app.add_url_rule('/{}/'.format(module_name), view_func=list_endpoint, methods=['GET'])
2629

30+
handle_endpoint = functools.partial(self.handle_remote_invocation, module)
31+
handle_endpoint.__name__ = "handle_" + module_name
32+
app.add_url_rule('/{}/'.format(module_name), view_func=handle_endpoint, methods=['POST'])
2733

28-
def list_implemented_methods(self, module_name):
29-
return json_response({
30-
'implemented_methods': self.router.list_implemented_methods(module_name)
31-
}, 200)
34+
def list_implemented_methods(self, module):
35+
methods = self.router.list_implemented_methods(module)
36+
return json_response({'implemented_methods': methods}, 200)
3237

33-
def handle_remote_invocation(self, module_name):
38+
def handle_remote_invocation(self, module):
3439
data = request.get_json()
35-
output = self.router.invoke_method(module_name=module_name, **data)
40+
output = self.router.invoke_method(module=module, **data)
3641
return json_response(output, 200)
3742

3843
def json_response(data, code):
3944
json_data = json.dumps(data, indent=None)
4045
response = current_app.response_class(json_data, mimetype='application/json; charset=UTF-8')
4146
response.status_code = code
4247

43-
return response
48+
return response

ubersmith_remote_module_server/router.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,10 @@
1313
# limitations under the License.
1414

1515
class Router(object):
16-
def __init__(self, modules, env_as_kwarg=True):
17-
self.modules = modules
16+
def __init__(self, env_as_kwarg=True):
1817
self.env_as_kwarg = env_as_kwarg
1918

20-
def invoke_method(self, module_name, method, params=None, env=None, callback=None):
19+
def invoke_method(self, module, method, params=None, env=None, callback=None):
2120
if params is None:
2221
params = []
2322
if env is None:
@@ -28,9 +27,7 @@ def invoke_method(self, module_name, method, params=None, env=None, callback=Non
2827
else:
2928
additional_kwargs = {}
3029

31-
module = self.modules[module_name]
3230
return getattr(module, method)(*params, **additional_kwargs)
3331

34-
def list_implemented_methods(self, module_name):
35-
module = self.modules[module_name]
32+
def list_implemented_methods(self, module):
3633
return [method for method in dir(module) if callable(getattr(module, method)) and not method.startswith('_')]

ubersmith_remote_module_server/server.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818

1919
class Server(object):
2020
def __init__(self, modules):
21-
self.router = router.Router(modules)
21+
self.router = router.Router()
2222
self.app = Flask(__name__)
23-
self.api = api.Api(self.app, self.router)
23+
self.api = api.Api(modules, self.app, self.router)
2424

2525
def run(self, *args, **kwargs):
2626
self.app.run(*args, **kwargs)

0 commit comments

Comments
 (0)