From 1f8748fe75c4285ada33674a12f3a5187fa4ee3e Mon Sep 17 00:00:00 2001 From: Qynn Swaan Date: Sun, 12 Feb 2023 17:38:18 -0500 Subject: [PATCH] [#676] [bukuserver API] improve tag replacement/deletion --- buku | 13 +++++++------ bukuserver/api.py | 23 +++++++++++++++-------- bukuserver/server.py | 4 ++-- tests/test_bukuDb.py | 2 +- tests/test_server.py | 13 +++++++++---- 5 files changed, 34 insertions(+), 21 deletions(-) diff --git a/buku b/buku index 32756e6e..7d722598 100755 --- a/buku +++ b/buku @@ -1959,7 +1959,7 @@ class BukuDb: return parse_tags(tags) - def replace_tag(self, orig: str, new: List[str] = []) -> bool: + def replace_tag(self, orig: str, new: List[str] = [], chatty: bool = True) -> bool: """Replace original tag by new tags in all records. Remove original tag if new tag is empty. @@ -1970,6 +1970,8 @@ class BukuDb: Original tag. new : list Replacement tags. + chatty: bool + Skip confirmation when set to False. Returns ------- @@ -1977,16 +1979,15 @@ class BukuDb: True on success, False on failure. """ - orig = delim_wrap(orig) - newtags = parse_tags(new) if new else DELIM + newtags = DELIM.join(sorted(set([s.lower().strip(DELIM) for s in new]))) if orig == newtags: print('Tags are same.') return False - # Remove original tag from DB if new tagset reduces to delimiter - if newtags == DELIM: - return self.delete_tag_at_index(0, orig) + # Remove original tag from DB if new tagset is empty + if newtags == '': + return self.delete_tag_at_index(0, orig, chatty=chatty) # Update bookmarks with original tag query = 'SELECT id, tags FROM bookmarks WHERE tags LIKE ?' diff --git a/bukuserver/api.py b/bukuserver/api.py index 171ffb8a..69384354 100644 --- a/bukuserver/api.py +++ b/bukuserver/api.py @@ -101,16 +101,23 @@ def get(self, tag: T.Optional[str]): return {"name": tag, "usage_count": tags[1][tag]} def put(self, tag: str): - bukudb = get_bukudb() try: - new_tags = request.data.get('tags') # type: ignore - if new_tags: - new_tags = new_tags.split(',') - else: - return response_bad() + new_tags = request.data.getlist('tags') # werkzeug.datastructures.MultiDict except AttributeError as e: - raise exceptions.ParseError(detail=str(e)) - return to_response(bukudb.replace_tag(tag, new_tags)) + new_tags = request.data.get('tags', []) # List[str] + if not isinstance(new_tags, list) or new_tags == []: + return response_bad() + bukudb = get_bukudb() + return to_response(bukudb.replace_tag(tag, new_tags, chatty=False)) + + def delete(self, tag: str): + bukudb = get_bukudb() + tags = search_tag(db=bukudb, stag=tag) + if tag not in tags[1]: + raise exceptions.NotFound() + if bukudb.delete_tag_at_index(0, tag, chatty=False): + return response_ok() + return response_bad() class ApiBookmarkView(MethodView): diff --git a/bukuserver/server.py b/bukuserver/server.py index 5b4be42f..af51d012 100644 --- a/bukuserver/server.py +++ b/bukuserver/server.py @@ -128,8 +128,8 @@ def shell_context(): # routing # api tag_api_view = api.ApiTagView.as_view('tag_api') - app.add_url_rule('/api/tags', defaults={'tag': None}, view_func=tag_api_view, methods=['GET']) - app.add_url_rule('/api/tags/', view_func=tag_api_view, methods=['GET', 'PUT']) + app.add_url_rule('/api/tags', defaults={'tag': None}, view_func=tag_api_view, methods=['GET'], strict_slashes=False) + app.add_url_rule('/api/tags/', view_func=tag_api_view, methods=['GET', 'PUT', 'DELETE']) bookmark_api_view = api.ApiBookmarkView.as_view('bookmark_api') app.add_url_rule('/api/bookmarks', defaults={'rec_id': None}, view_func=bookmark_api_view, methods=['GET', 'POST', 'DELETE']) app.add_url_rule('/api/bookmarks/', view_func=bookmark_api_view, methods=['GET', 'PUT', 'DELETE']) diff --git a/tests/test_bukuDb.py b/tests/test_bukuDb.py index cfe34959..3f16ce3d 100644 --- a/tests/test_bukuDb.py +++ b/tests/test_bukuDb.py @@ -708,7 +708,7 @@ def test_replace_tag(self): with mock.patch("builtins.input", return_value="y"): self.bdb.replace_tag("news", ["__01"]) with mock.patch("builtins.input", return_value="y"): - self.bdb.replace_tag("zażółć", ["__02,__03"]) + self.bdb.replace_tag("zażółć", ["__02", "__03"]) # replacing tag which is also a substring of other tag with mock.patch("builtins.input", return_value="y"): diff --git a/tests/test_server.py b/tests/test_server.py index 90a157e8..4bf454f8 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -86,22 +86,27 @@ def test_tag_api(client): rd = client.get('/api/tags/tag1') assert rd.status_code == 200 assert rd.get_json() == {'name': 'tag1', 'usage_count': 1} - rd = client.put('/api/tags/tag1', data={'tags': 'tag3,tag4'}) + rd = client.put('/api/tags/tag1', data={'tags': ['tag3', 'tag4']}) + print("\r\nrd", rd.__dict__, "\r\n") assert rd.status_code == 200 assert rd.get_json() == response_template['success'] rd = client.get('/api/tags') assert rd.status_code == 200 - assert rd.get_json() == {'tags': ['tag2', 'tag3 tag4']} + assert rd.get_json() == {'tags': ['tag2', 'tag3', 'tag4']} rd = client.put('/api/tags/tag2', data={'tags': 'tag5'}) assert rd.status_code == 200 assert rd.get_json() == response_template['success'] rd = client.get('/api/tags') assert rd.status_code == 200 - assert rd.get_json() == {'tags': ['tag3 tag4', 'tag5']} + assert rd.get_json() == {'tags': ['tag3', 'tag4', 'tag5']} + assert rd.status_code == 200 + rd = client.delete('/api/tags/tag4') + assert rd.status_code == 200 + assert rd.get_json() == response_template['success'] rd = client.get('/api/bookmarks/1') assert rd.status_code == 200 assert rd.get_json() == { - 'description': '', 'tags': ['tag3 tag4', 'tag5'], 'title': 'Google', + 'description': '', 'tags': ['tag3', 'tag5'], 'title': 'Google', 'url': 'http://google.com'}