From 971a9be64ca407d7b83b2a8c44599449000d2b1f Mon Sep 17 00:00:00 2001 From: Max Nikulin Date: Sun, 13 Sep 2020 09:50:00 +0700 Subject: [PATCH 1/2] Obtain preferred response format from Accept header A step toward external authentication application to allow proper response without digging into webscrapbook implementation details #177. --- webscrapbook/app.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/webscrapbook/app.py b/webscrapbook/app.py index 7b31e939..c5e01b99 100644 --- a/webscrapbook/app.py +++ b/webscrapbook/app.py @@ -585,7 +585,13 @@ def action(self): @cached_property def format(self): """Shortcut of the requested format.""" - rv = request.values.get('f') + rv = None + best_accept = request.accept_mimetypes.best + if best_accept == 'application/json': + rv = 'json' + elif best_accept == 'text/event-stream': + rv = 'sse' + rv = request.values.get('f', default=rv) rv = request.values.get('format', default=rv) return rv From 295e7366fbedb16a2cb235f455e7cd239a7f3e55 Mon Sep 17 00:00:00 2001 From: Max Nikulin Date: Tue, 15 Sep 2020 12:33:34 +0700 Subject: [PATCH 2/2] Add unit tests for Accept header Part of #177. --- test/test_app_actions.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/test/test_app_actions.py b/test/test_app_actions.py index beea55f8..250b8171 100644 --- a/test/test_app_actions.py +++ b/test/test_app_actions.py @@ -677,6 +677,31 @@ def test_permission_check2(self, mock_abort): except FileNotFoundError: pass + def test_accept_format(self): + """Test that `Accept` HTTP header causes change of response format + + - Default `text/html` is tested by `TestView.test_directory`. + - `f=json` parameter is tested by `test_directory` in this class. + """ + with app.test_client() as c: + + r = c.get( + '/subdir/', + query_string={'a': 'info'}, + headers=[('Accept', 'application/json')]) + self.assertEqual(r.status_code, 200) + self.assertEqual(r.headers['Content-Type'], 'application/json') + self.assertEqual(r.json, { + 'success': True, + 'data': { + 'name': 'subdir', + 'type': 'dir', + 'size': None, + 'last_modified': os.stat(os.path.join(server_root, 'subdir')).st_mtime, + 'mime': None, + }, + }) + def test_directory(self): with app.test_client() as c: r = c.get('/subdir', query_string={'a': 'info', 'f': 'json'}) @@ -1263,6 +1288,18 @@ def test_sse_directory(self): os.stat(os.path.join(server_root, 'subdir', 'sub')).st_mtime) in data) self.assertTrue(data.endswith('\n\nevent: complete\ndata: \n\n')) + headers=[('Accept', 'text/event-stream')] + with self.subTest(headers=headers): + r = c.get('/subdir/', query_string={'a': 'list'}, headers=headers) + self.assertEqual(r.status_code, 200) + self.assertEqual(r.headers['Content-Type'], 'text/event-stream; charset=utf-8') + data = r.data.decode('UTF-8') + self.assertTrue('data: {{"name": "file.txt", "type": "file", "size": 3, "last_modified": {}}}\n\n'.format( + os.stat(os.path.join(server_root, 'subdir', 'file.txt')).st_mtime) in data) + self.assertTrue('data: {{"name": "sub", "type": "dir", "size": null, "last_modified": {}}}\n\n'.format( + os.stat(os.path.join(server_root, 'subdir', 'sub')).st_mtime) in data) + self.assertTrue(data.endswith('\n\nevent: complete\ndata: \n\n')) + def test_sse_directory_recursive(self): with app.test_client() as c: r = c.get('/subdir', query_string={'a': 'list', 'f': 'sse', 'recursive': 1})