Skip to content

Commit a7a31d6

Browse files
committed
[#676] [bukuserver API] improve api views
1 parent 3818639 commit a7a31d6

File tree

8 files changed

+422
-249
lines changed

8 files changed

+422
-249
lines changed

buku

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1959,7 +1959,7 @@ class BukuDb:
19591959

19601960
return parse_tags(tags)
19611961

1962-
def replace_tag(self, orig: str, new: List[str] = []) -> bool:
1962+
def replace_tag(self, orig: str, new: List[str] = []):
19631963
"""Replace original tag by new tags in all records.
19641964
19651965
Remove original tag if new tag is empty.
@@ -1971,22 +1971,26 @@ class BukuDb:
19711971
new : list
19721972
Replacement tags.
19731973
1974-
Returns
1974+
Raises
19751975
-------
1976-
bool
1977-
True on success, False on failure.
1976+
ValueError: Invalid input(s) provided.
1977+
RuntimeError: Tag deletion failed.
1978+
19781979
"""
19791980

1981+
if DELIM in orig:
1982+
raise ValueError("Original tag cannot contain delimiter ({}).".format(DELIM))
1983+
19801984
orig = delim_wrap(orig)
1981-
newtags = parse_tags(new) if new else DELIM
1985+
newtags = parse_tags([DELIM.join(new)])
19821986

19831987
if orig == newtags:
1984-
print('Tags are same.')
1985-
return False
1988+
raise ValueError("Original and replacement tags are the same.")
19861989

19871990
# Remove original tag from DB if new tagset reduces to delimiter
19881991
if newtags == DELIM:
1989-
return self.delete_tag_at_index(0, orig)
1992+
if not self.delete_tag_at_index(0, orig):
1993+
raise RuntimeError("Tag deletion failed.")
19901994

19911995
# Update bookmarks with original tag
19921996
query = 'SELECT id, tags FROM bookmarks WHERE tags LIKE ?'
@@ -2002,8 +2006,6 @@ class BukuDb:
20022006

20032007
self.conn.commit()
20042008

2005-
return True
2006-
20072009
def get_tagstr_from_taglist(self, id_list, taglist):
20082010
"""Get a string of delimiter-separated (and enclosed) string
20092011
of tags from a dictionary of tags by matching ids.
@@ -5974,7 +5976,11 @@ POSITIONAL ARGUMENTS:
59745976
if len(args.replace) == 1:
59755977
bdb.delete_tag_at_index(0, args.replace[0])
59765978
else:
5977-
bdb.replace_tag(args.replace[0], args.replace[1:])
5979+
try:
5980+
bdb.replace_tag(args.replace[0], [' '.join(args.replace[1:])])
5981+
except Exception as e:
5982+
LOGERR(str(e))
5983+
bdb.close_quit(1)
59785984

59795985
# Export bookmarks
59805986
if args.export is not None and not search_opted:

bukuserver/api.py

Lines changed: 85 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,23 @@
66
from unittest import mock
77

88
from flask.views import MethodView
9-
from flask_api import exceptions, status
109

1110
import buku
1211
from buku import BukuDb
1312

1413
import flask
15-
from flask import current_app, jsonify, redirect, request, url_for
14+
from flask import current_app, redirect, request, url_for
1615

1716
try:
18-
from . import forms, response
17+
from response import Response
18+
from forms import ApiBookmarkCreateForm, ApiBookmarkEditForm, ApiBookmarkRangeEditForm, ApiTagForm
1919
except ImportError:
20-
from bukuserver import forms, response
20+
from bukuserver.response import Response
21+
from bukuserver.forms import ApiBookmarkCreateForm, ApiBookmarkEditForm, ApiBookmarkRangeEditForm, ApiTagForm
2122

2223

2324
STATISTIC_DATA = None
2425

25-
response_ok = lambda: (jsonify(response.response_template['success']),
26-
status.HTTP_200_OK,
27-
{'ContentType': 'application/json'})
28-
response_bad = lambda: (jsonify(response.response_template['failure']),
29-
status.HTTP_400_BAD_REQUEST,
30-
{'ContentType': 'application/json'})
31-
to_response = lambda ok: response_ok() if ok else response_bad()
3226

3327
def entity(bookmark, id=False):
3428
data = {
@@ -94,23 +88,35 @@ class ApiTagView(MethodView):
9488
def get(self, tag: T.Optional[str]):
9589
bukudb = get_bukudb()
9690
if tag is None:
97-
return {"tags": search_tag(db=bukudb, limit=5)[0]}
91+
return Response.SUCCESS(data={"tags": search_tag(db=bukudb, limit=5)[0]})
9892
tags = search_tag(db=bukudb, stag=tag)
9993
if tag not in tags[1]:
100-
raise exceptions.NotFound()
101-
return {"name": tag, "usage_count": tags[1][tag]}
94+
return Response.TAG_NOT_FOUND()
95+
return Response.SUCCESS(data={"name": tag, "usage_count": tags[1][tag]})
10296

10397
def put(self, tag: str):
98+
form = ApiTagForm({})
99+
error_response, data = form.process_data(request.get_json())
100+
if error_response is not None:
101+
return error_response(data=data)
104102
bukudb = get_bukudb()
103+
tags = search_tag(db=bukudb, stag=tag)
104+
if tag not in tags[1]:
105+
return Response.TAG_NOT_FOUND()
105106
try:
106-
new_tags = request.data.get('tags') # type: ignore
107-
if new_tags:
108-
new_tags = new_tags.split(',')
109-
else:
110-
return response_bad()
111-
except AttributeError as e:
112-
raise exceptions.ParseError(detail=str(e))
113-
return to_response(bukudb.replace_tag(tag, new_tags))
107+
bukudb.replace_tag(tag, form.tags.data)
108+
return Response.SUCCESS()
109+
except (ValueError, RuntimeError):
110+
return Response.FAILURE()
111+
112+
def delete(self, tag: str):
113+
if buku.DELIM in tag:
114+
return Response.TAG_NOT_VALID()
115+
bukudb = get_bukudb()
116+
tags = search_tag(db=bukudb, stag=tag)
117+
if tag not in tags[1]:
118+
return Response.TAG_NOT_FOUND()
119+
return Response.from_flag(bukudb.delete_tag_at_index(0, tag, chatty=False))
114120

115121

116122
class ApiBookmarkView(MethodView):
@@ -121,34 +127,40 @@ def get(self, rec_id: T.Union[int, None]):
121127
all_bookmarks = bukudb.get_rec_all()
122128
result = {'bookmarks': [entity(bookmark, id=not request.path.startswith('/api/'))
123129
for bookmark in all_bookmarks]}
124-
res = jsonify(result)
125130
else:
126131
bukudb = getattr(flask.g, 'bukudb', get_bukudb())
127132
bookmark = bukudb.get_rec_by_id(rec_id)
128-
res = (response_bad() if bookmark is None else jsonify(entity(bookmark)))
129-
return res
133+
if bookmark is None:
134+
return Response.BOOKMARK_NOT_FOUND()
135+
result = entity(bookmark)
136+
return Response.SUCCESS(data=result)
130137

131138
def post(self, rec_id: None = None):
139+
form = ApiBookmarkCreateForm({})
140+
error_response, error_data = form.process_data(request.get_json())
141+
if error_response is not None:
142+
return error_response(data=error_data)
132143
bukudb = getattr(flask.g, 'bukudb', get_bukudb())
133-
create_bookmarks_form = forms.ApiBookmarkForm()
134-
url_data = create_bookmarks_form.url.data
135144
result_flag = bukudb.add_rec(
136-
url_data,
137-
create_bookmarks_form.title.data,
138-
create_bookmarks_form.tags.data,
139-
create_bookmarks_form.description.data
140-
)
141-
return to_response(result_flag)
145+
form.url.data,
146+
form.title.data,
147+
form.tags_str,
148+
form.description.data)
149+
return Response.from_flag(result_flag)
142150

143151
def put(self, rec_id: int):
152+
form = ApiBookmarkEditForm({})
153+
error_response, error_data = form.process_data(request.get_json())
154+
if error_response is not None:
155+
return error_response(data=error_data)
144156
bukudb = getattr(flask.g, 'bukudb', get_bukudb())
145157
result_flag = bukudb.update_rec(
146158
rec_id,
147-
request.form.get('url'),
148-
request.form.get('title'),
149-
request.form.get('tags'),
150-
request.form.get('description'))
151-
return to_response(result_flag)
159+
form.url.data,
160+
form.title.data,
161+
form.tags_str,
162+
form.description.data)
163+
return Response.from_flag(result_flag)
152164

153165
def delete(self, rec_id: T.Union[int, None]):
154166
if rec_id is None:
@@ -158,45 +170,57 @@ def delete(self, rec_id: T.Union[int, None]):
158170
else:
159171
bukudb = getattr(flask.g, 'bukudb', get_bukudb())
160172
result_flag = bukudb.delete_rec(rec_id)
161-
return to_response(result_flag)
173+
return Response.from_flag(result_flag)
162174

163175

164176
class ApiBookmarkRangeView(MethodView):
165177

166178
def get(self, starting_id: int, ending_id: int):
167179
bukudb = getattr(flask.g, 'bukudb', get_bukudb())
168180
max_id = bukudb.get_max_id() or 0
169-
if starting_id > max_id or ending_id > max_id:
170-
return response_bad()
181+
if starting_id > ending_id or ending_id > max_id:
182+
return Response.RANGE_NOT_VALID()
171183
result = {'bookmarks': {i: entity(bukudb.get_rec_by_id(i))
172184
for i in range(starting_id, ending_id + 1)}}
173-
return jsonify(result)
185+
return Response.SUCCESS(data=result)
174186

175187
def put(self, starting_id: int, ending_id: int):
176188
bukudb = getattr(flask.g, 'bukudb', get_bukudb())
177189
max_id = bukudb.get_max_id() or 0
178-
if starting_id > max_id or ending_id > max_id:
179-
return response_bad()
180-
for i in range(starting_id, ending_id + 1, 1):
181-
updated_bookmark = request.data.get(str(i)) # type: ignore
182-
result_flag = bukudb.update_rec(
183-
i,
184-
updated_bookmark.get('url'),
185-
updated_bookmark.get('title'),
186-
updated_bookmark.get('tags'),
187-
updated_bookmark.get('description'))
188-
if result_flag is False:
189-
return response_bad()
190-
return response_ok()
190+
if starting_id > ending_id or ending_id > max_id:
191+
return Response.RANGE_NOT_VALID()
192+
updates = []
193+
errors = {}
194+
for rec_id in range(starting_id, ending_id + 1):
195+
json = request.get_json().get(str(rec_id))
196+
if json is None:
197+
errors[rec_id] = 'Input required.'
198+
continue
199+
form = ApiBookmarkRangeEditForm({})
200+
error_response, error_data = form.process_data(json)
201+
if error_response is not None:
202+
errors[rec_id] = error_data.get('errors')
203+
updates += [{'index': rec_id,
204+
'url': form.url.data,
205+
'title_in': form.title.data,
206+
'tags_in': form.tags_in,
207+
'desc': form.description.data}]
208+
209+
if errors:
210+
return Response.INPUT_NOT_VALID(data={'errors': errors})
211+
for update in updates:
212+
if not bukudb.update_rec(**update):
213+
return Response.FAILURE()
214+
return Response.SUCCESS()
191215

192216
def delete(self, starting_id: int, ending_id: int):
193217
bukudb = getattr(flask.g, 'bukudb', get_bukudb())
194218
max_id = bukudb.get_max_id() or 0
195-
if starting_id > max_id or ending_id > max_id:
196-
return response_bad()
219+
if starting_id > ending_id or ending_id > max_id:
220+
return Response.RANGE_NOT_VALID()
197221
idx = min([starting_id, ending_id])
198222
result_flag = bukudb.delete_rec(idx, starting_id, ending_id, is_range=True)
199-
return to_response(result_flag)
223+
return Response.from_flag(result_flag)
200224

201225

202226
class ApiBookmarkSearchView(MethodView):
@@ -217,14 +241,11 @@ def get(self):
217241
)
218242
deep = deep if isinstance(deep, bool) else deep.lower() == 'true'
219243
regex = regex if isinstance(regex, bool) else regex.lower() == 'true'
220-
221244
bukudb = getattr(flask.g, 'bukudb', get_bukudb())
222-
res = None
223245
result = {'bookmarks': [entity(bookmark, id=True)
224246
for bookmark in bukudb.searchdb(keywords, all_keywords, deep, regex)]}
225247
current_app.logger.debug('total bookmarks:{}'.format(len(result['bookmarks'])))
226-
res = jsonify(result)
227-
return res
248+
return Response.SUCCESS(data=result)
228249

229250
def delete(self):
230251
arg_obj = request.form
@@ -246,8 +267,8 @@ def delete(self):
246267
res = None
247268
for bookmark in bukudb.searchdb(keywords, all_keywords, deep, regex):
248269
if not bukudb.delete_rec(bookmark.id):
249-
res = response_bad()
250-
return res or response_ok()
270+
res = Response.FAILURE()
271+
return res or Response.SUCCESS()
251272

252273

253274
class BookmarkletView(MethodView): # pylint: disable=too-few-public-methods

0 commit comments

Comments
 (0)