diff --git a/debian/changelog b/debian/changelog index 33cce52657533ef149c08e421da33cd4af2e1595..34b8df893da005e5c125fa19522f19a6857cc429 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,12 @@ +python3-sams-classes (1.0.1~1) UNRELEASED; urgency=medium + + * fixed issue #20 "Basis-Pfad (/) bei API über Proxy nicht aufrufbar" + + -- Lobinger <slobinger@justus.zib.de> Tue, 13 Jun 2017 15:28:43 +0200 + python3-sams-classes (1.0.0) unstable; urgency=medium * Initial Release. -- Sebastian Lobinger <sebastian.lobinger@zib.de> Thu, 08 Jun 2017 10:42:38 +0200 - \ No newline at end of file + diff --git a/sams_classes/sams_app.py b/sams_classes/sams_app.py index 07735b8e0829772aa01ec1cd8bf9a66b628f0687..26f203e3d55ce596ae83f9d3f2cd27ca09cfd194 100644 --- a/sams_classes/sams_app.py +++ b/sams_classes/sams_app.py @@ -46,14 +46,18 @@ class SAMSApp: endpoint = '_'.join(view['function'].split('.')) view_func = SAMSApp._get_attr(self.module, view['function']) self.__blueprint.add_url_rule( - rule = self._generate_url(view.get('url')), endpoint = endpoint, + rule = self._generate_url(urlPart = view.get('url')), endpoint = endpoint, view_func = view_func) def __add_proxy_urls(self): i = 0 for proxy in self.proxies: self.__blueprint.add_url_rule( - rule = self._generate_url(proxy.urlRule), + rule = self._generate_url(urlPart = proxy.urlRule), + endpoint = self.__blueprint.name.replace('.', '_') + '_proxy_' + str(i), + view_func = proxy.proxy) + self.__blueprint.add_url_rule( + rule = self._generate_url(urlPart = proxy.rootUrlRule), endpoint = self.__blueprint.name.replace('.', '_') + '_proxy_' + str(i), view_func = proxy.proxy) i += 1 @@ -107,15 +111,12 @@ class SAMSApp: return entries def _generate_url( - self, urlPrefix: str, urlPart: str = '', external = False ) -> str: + self, urlPrefix: str = '', urlPart: str = '', external = False ) -> str: if external: return urlPart urlPrefix = urlPrefix.strip('/') - urlPart = urlPart.strip('/') - urlElements = [] - if urlPrefix: - urlElements.append(urlPrefix) - if urlPart: - urlElements.append(urlPart) - url = '/'.join(urlElements) - return '/' + url if (not url) or (url[0] is not '/') else url \ No newline at end of file + urlPart = urlPart.lstrip('/') + url = '/'.join([urlPrefix, urlPart]) + if url[0] != '/': + url = '/' + url + return url \ No newline at end of file diff --git a/sams_classes/sams_proxy.py b/sams_classes/sams_proxy.py index 009032742578668c155e2fd4327bb63874b245f0..b95ecedd763ec2c1d7d89828ddc9cb99322354e9 100644 --- a/sams_classes/sams_proxy.py +++ b/sams_classes/sams_proxy.py @@ -22,8 +22,16 @@ class SAMSProxy: if 0 == len(self._proxySpec['in']): return '/<path:path>' return '/' + urlRule + '/<path:path>' + + @property + def rootUrlRule(self): + urlRule = self._proxySpec['in'].strip() + urlRule = urlRule.strip('/') + if 0 == len(self._proxySpec['in']): + return '/' + return '/' + urlRule + '/' - def proxy(self, path): + def proxy(self, path = ''): url = '/'.join([self._proxySpec['out'].strip().rstrip('/'), path]) headers = SAMSProxy.__cleaned_dict(request.headers) headers.pop('Content-Type', None) diff --git a/test/api_test_server.py b/test/api_test_server.py index ed38380313c21eeb8e9995c8aa8c31ad56e0cf9f..049e5a541658d08d31b22c4b98f57e3da95eea01 100644 --- a/test/api_test_server.py +++ b/test/api_test_server.py @@ -21,4 +21,9 @@ def passthrough(): body = request.get_json() output = json.dumps( {'url': request.args, 'body': body, 'header': dict(request.headers)}) - return output \ No newline at end of file + return output + +@app.route('/', methods = [ + 'GET', 'POST', 'DELETE', 'UPDATE', 'PATCH', 'OPTIONS', 'PUT', 'HEAD']) +def rootpath(): + return 'rootpath' \ No newline at end of file diff --git a/test/test_sams_app.py b/test/test_sams_app.py index a5288aa8680937fece5b46f5e62ebf19a6ea7f7b..3f70196207b0805c525f848a77403508b9c7c8c4 100644 --- a/test/test_sams_app.py +++ b/test/test_sams_app.py @@ -153,7 +153,7 @@ class TestSAMSApp(unittest.TestCase): app = SAMSApp(name = 'test', manifest = manifest , langDict = {'en': langDict}) menu = app.menu(langCode = 'en', urlPrefix = '/test') - self.assertEqual(menu[0]['url'], '/test') + self.assertEqual(menu[0]['url'], '/test/') self.assertEqual(menu[1]['url'], '/test/1') def test_app_menu_external_urls(self): @@ -168,7 +168,7 @@ class TestSAMSApp(unittest.TestCase): app = SAMSApp(name = 'test', manifest = manifest , langDict = {'en': langDict}) menu = app.menu(langCode = 'en', urlPrefix = '/test') - self.assertEqual(menu[0]['url'], '/test') + self.assertEqual(menu[0]['url'], '/test/') self.assertEqual(menu[1]['url'], 'http://zib.de') def test_app_proxies_list(self): @@ -218,10 +218,11 @@ class TestSAMSAppWithThreadedApi(unittest.TestCase): ) self.flaskApp.register_blueprint(testApp.blueprint) self.assertGreater(len(list(self.flaskApp.url_map.iter_rules())), 1) - eprint(self.flaskApp.url_map) - with self.flaskApp.test_client(self) as client: - response = client.get('/api/hello') - self.assertIs(response.status_code, 200) + for path in ('/api/hello', '/api/'): + with self.subTest(path): + with self.flaskApp.test_client(self) as client: + response = client.get(path) + self.assertIs(response.status_code, 200) if __name__ == '__main__': unittest.main() \ No newline at end of file diff --git a/test/test_sams_proxy.py b/test/test_sams_proxy.py index 8390ccffea162947ec9496c376a8dc4dce70cd5b..a46d2d5411bbe300e2eb467c29bb126eb46402a7 100644 --- a/test/test_sams_proxy.py +++ b/test/test_sams_proxy.py @@ -68,6 +68,9 @@ class TestSAMSHubWithThreadedAPI(unittest.TestCase): time.sleep(0.01) self.proxyApp = Flask('test') self.proxyApp.config['TESTING'] = True + self.basic_url_rule_params = {'endpoint': 'proxy', + 'methods': ['GET', 'POST', 'PUT', 'UPDATE', 'PATCH', 'DELETE', 'OPTIONS']} + self.url_rule_properties = ['rootUrlRule', 'urlRule'] def tearDown(self): response = requests.get('http://localhost:4711/shutdown') @@ -78,9 +81,9 @@ class TestSAMSHubWithThreadedAPI(unittest.TestCase): """The proxy passes json data """ proxy = SAMSProxy( proxySpec = {'in': '/proxy', 'out': 'http://localhost:4711'}) - self.proxyApp.add_url_rule(rule = proxy.urlRule, endpoint = 'proxy', - view_func = proxy.proxy, methods=['GET', 'POST', 'PUT', 'UPDATE', - 'PATCH', 'DELETE', 'OPTIONS']) + for urlRuleProp in self.url_rule_properties: + self.proxyApp.add_url_rule(rule = getattr(proxy, urlRuleProp), + view_func = proxy.proxy, **self.basic_url_rule_params) thProxyApp = FlaskInThread(self.proxyApp, host="localhost", port=5000) thProxyApp.start() time.sleep(0.01) @@ -99,9 +102,9 @@ class TestSAMSHubWithThreadedAPI(unittest.TestCase): 'user': {'name': 'u','param-type': 'url'}, 'group': {'name': 'g', 'param-type': 'body'}, 'language': {'name': 'Lang', 'param-type': 'header'}}}}) - self.proxyApp.add_url_rule(rule = proxy.urlRule, endpoint = 'proxy', - view_func = proxy.proxy, methods=['GET', 'POST', 'PUT', 'UPDATE', - 'PATCH', 'DELETE', 'OPTIONS']) + for urlRuleProp in self.url_rule_properties: + self.proxyApp.add_url_rule(rule = getattr(proxy, urlRuleProp), + view_func = proxy.proxy, **self.basic_url_rule_params) self.proxyApp.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT' with self.proxyApp.test_client(self) as client: with client.session_transaction() as sess: @@ -125,9 +128,9 @@ class TestSAMSHubWithThreadedAPI(unittest.TestCase): 'user': {'name': 'u','param-type': 'url'}, 'group': {'name': 'g', 'param-type': 'body'}, 'language': {'name': 'lang', 'param-type': 'header'}}}}) - self.proxyApp.add_url_rule(rule = proxy.urlRule, endpoint = 'proxy', - view_func = proxy.proxy, methods=['GET', 'POST', 'PUT', 'UPDATE', - 'PATCH', 'DELETE', 'OPTIONS']) + for urlRuleProp in self.url_rule_properties: + self.proxyApp.add_url_rule(rule = getattr(proxy, urlRuleProp), + view_func = proxy.proxy, **self.basic_url_rule_params) self.proxyApp.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT' with self.proxyApp.test_client(self) as client: with client.session_transaction() as sess: @@ -152,9 +155,9 @@ class TestSAMSHubWithThreadedAPI(unittest.TestCase): 'user': {'name': 'u','param-type': 'url'}, 'group': {'name': 'g', 'param-type': 'body'}, 'language': {'name': 'lang', 'param-type': 'header'}}}}) - self.proxyApp.add_url_rule(rule = proxy.urlRule, endpoint = 'proxy', - view_func = proxy.proxy, methods=['GET', 'POST', 'PUT', 'UPDATE', - 'PATCH', 'DELETE', 'OPTIONS']) + for urlRuleProp in self.url_rule_properties: + self.proxyApp.add_url_rule(rule = getattr(proxy, urlRuleProp), + view_func = proxy.proxy, **self.basic_url_rule_params) self.proxyApp.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT' with self.proxyApp.test_client(self) as client: with client.session_transaction() as sess: @@ -171,23 +174,25 @@ class TestSAMSHubWithThreadedAPI(unittest.TestCase): """ The proxy forwards a request to test flaskapp and returns 'hello' """ proxy = SAMSProxy( proxySpec = {'in': '/proxy', 'out': 'http://localhost:4711'}) - self.proxyApp.add_url_rule(rule = proxy.urlRule, endpoint = 'proxy', - view_func = proxy.proxy, methods=['GET', 'POST', 'PUT', 'UPDATE', - 'PATCH', 'DELETE', 'OPTIONS']) + for urlRuleProp in self.url_rule_properties: + self.proxyApp.add_url_rule(rule = getattr(proxy, urlRuleProp), + view_func = proxy.proxy, **self.basic_url_rule_params) with self.proxyApp.test_client(self) as client: + eprint(self.proxyApp.url_map) for method in ['get', 'post', 'put', 'update', 'patch', 'delete', 'options']: - response = client.open(path='/proxy/hello', method=method.upper()) - self.assertIs(response.status_code, 200, method) - self.assertEqual(response.data.decode('utf-8'), method + ' hello') + for path in ('/proxy/hello', '/proxy/'): + response = client.open(path = path, method = method.upper()) + with self.subTest('method ' + method + ' path ' + path): + self.assertIs(response.status_code, 200, method) def test_param_passthrough(self): """The proxy passes headers, form / data and url params """ proxy = SAMSProxy( proxySpec = {'in': '/proxy', 'out': 'http://localhost:4711'}) - self.proxyApp.add_url_rule(rule = proxy.urlRule, endpoint = 'proxy', - view_func = proxy.proxy, methods=['GET', 'POST', 'PUT', 'UPDATE', - 'PATCH', 'DELETE', 'OPTIONS']) + for urlRuleProp in self.url_rule_properties: + self.proxyApp.add_url_rule(rule = getattr(proxy, urlRuleProp), + view_func = proxy.proxy, **self.basic_url_rule_params) with self.proxyApp.test_client(self) as client: res = client.get(path='/proxy/passthrough', query_string={'u':'foo'}, data={'g':'bar'}, headers={'lang': 'klingon'}) @@ -214,6 +219,9 @@ class TestSAMSHubWithThreadedAPI(unittest.TestCase): proxyApp.add_url_rule(rule = proxy.urlRule, endpoint = 'proxy', view_func = proxy.proxy, methods=['GET', 'POST', 'PUT', 'UPDATE', 'PATCH', 'DELETE', 'OPTIONS']) + proxyApp.add_url_rule(rule = proxy.rootUrlRule, endpoint = 'proxy', + view_func = proxy.proxy, methods=['GET', 'POST', 'PUT', 'UPDATE', + 'PATCH', 'DELETE', 'OPTIONS']) with proxyApp.test_client(self) as client: res = client.get(path='/proxy/passthrough', query_string={'u':'foo'}, data={'g':'bar'},