1
0
mirror of https://github.com/spl0k/supysonic.git synced 2024-12-22 17:06:17 +00:00

Reworked star/unstar error handling

Don't stop at the first error anymore, instead treat all provided ids
Potential errors are aggregated, if there's only one it is returned
as-is, if there are more they are all nested in a top-level error whose
code is 0 if there are different error codes, or their value if all
error codes are the same.
This error-nesting doesn't validate against the XSD, but the Subsonic
API gives absolutely no information on how errors should be handled.
And reverse-engineering is not in my line of work
This commit is contained in:
spl0k 2017-11-14 23:16:58 +01:00
parent 5084c745dc
commit da857b9ceb

View File

@ -28,84 +28,108 @@ from supysonic.db import Track, Album, Artist, Folder
from supysonic.db import StarredTrack, StarredAlbum, StarredArtist, StarredFolder from supysonic.db import StarredTrack, StarredAlbum, StarredArtist, StarredFolder
from supysonic.db import RatingTrack, RatingFolder from supysonic.db import RatingTrack, RatingFolder
def try_star(ent, starred_ent, eid):
""" Stars an entity
:param ent: entity class, Folder, Artist, Album or Track
:param starred_ent: class used for the db representation of the starring of ent
:param eid: id of the entity to star
:return error dict, if any. None otherwise
"""
try:
uid = uuid.UUID(eid)
except:
return { 'code': 0, 'message': 'Invalid {} id {}'.format(ent.__name__, eid) }
if store.get(starred_ent, (request.user.id, uid)):
return { 'code': 0, 'message': '{} {} already starred'.format(ent.__name__, eid) }
e = store.get(ent, uid)
if not e:
return { 'code': 70, 'message': 'Unknown {} id {}'.format(ent.__name__, eid) }
starred = starred_ent()
starred.user_id = request.user.id
starred.starred_id = uid
store.add(starred)
return None
def try_unstar(starred_ent, eid):
""" Unstars an entity
:param starred_ent: class used for the db representation of the starring of the entity
:param eid: id of the entity to unstar
:return error dict, if any. None otherwise
"""
try:
uid = uuid.UUID(eid)
except:
return { 'code': 0, 'message': 'Invalid id {}'.format(eid) }
store.find(starred_ent, starred_ent.user_id == request.user.id, starred_ent.starred_id == uid).remove()
return None
def merge_errors(errors):
error = None
errors = filter(None, errors)
if len(errors) == 1:
error = errors[0]
elif len(errors) > 1:
codes = set(map(lambda e: e['code'], errors))
error = { 'code': list(codes)[0] if len(codes) == 1 else 0, 'error': errors }
return error
@app.route('/rest/star.view', methods = [ 'GET', 'POST' ]) @app.route('/rest/star.view', methods = [ 'GET', 'POST' ])
def star(): def star():
id, albumId, artistId = map(request.values.getlist, [ 'id', 'albumId', 'artistId' ]) id, albumId, artistId = map(request.values.getlist, [ 'id', 'albumId', 'artistId' ])
def try_star(ent, starred_ent, eid): if not id and not albumId and not artistId:
try: return request.error_formatter(10, 'Missing parameter')
uid = uuid.UUID(eid)
except:
return 2, request.error_formatter(0, 'Invalid %s id' % ent.__name__)
if store.get(starred_ent, (request.user.id, uid)):
return 2, request.error_formatter(0, '%s already starred' % ent.__name__)
e = store.get(ent, uid)
if e:
starred = starred_ent()
starred.user_id = request.user.id
starred.starred_id = uid
store.add(starred)
else:
return 1, request.error_formatter(70, 'Unknown %s id' % ent.__name__)
return 0, None
errors = []
for eid in id: for eid in id:
err, ferror = try_star(Track, StarredTrack, eid) terr = try_star(Track, StarredTrack, eid)
if err == 1: ferr = try_star(Folder, StarredFolder, eid)
err, ferror = try_star(Folder, StarredFolder, eid) if terr and ferr:
if err: errors += [ terr, ferr ]
return ferror
elif err == 2:
return ferror
for alId in albumId: for alId in albumId:
err, ferror = try_star(Album, StarredAlbum, alId) errors.append(try_star(Album, StarredAlbum, alId))
if err:
return ferror
for arId in artistId: for arId in artistId:
err, ferror = try_star(Artist, StarredArtist, arId) errors.append(try_star(Artist, StarredArtist, arId))
if err:
return ferror
store.commit() store.commit()
return request.formatter({}) error = merge_errors(errors)
return request.formatter({ 'error': error }, error = True) if error else request.formatter({})
@app.route('/rest/unstar.view', methods = [ 'GET', 'POST' ]) @app.route('/rest/unstar.view', methods = [ 'GET', 'POST' ])
def unstar(): def unstar():
id, albumId, artistId = map(request.values.getlist, [ 'id', 'albumId', 'artistId' ]) id, albumId, artistId = map(request.values.getlist, [ 'id', 'albumId', 'artistId' ])
def try_unstar(ent, eid): if not id and not albumId and not artistId:
try: return request.error_formatter(10, 'Missing parameter')
uid = uuid.UUID(eid)
except:
return request.error_formatter(0, 'Invalid id')
store.find(ent, ent.user_id == request.user.id, ent.starred_id == uid).remove()
return None
errors = []
for eid in id: for eid in id:
err = try_unstar(StarredTrack, eid) terr = try_unstar(StarredTrack, eid)
if err: ferr = try_unstar(StarredFolder, eid)
return err if terr and ferr:
err = try_unstar(StarredFolder, eid) errors += [ terr, ferr ]
if err:
return err
for alId in albumId: for alId in albumId:
err = try_unstar(StarredAlbum, alId) errors.append(try_unstar(StarredAlbum, alId))
if err:
return err
for arId in artistId: for arId in artistId:
err = try_unstar(StarredArtist, arId) errors.append(try_unstar(StarredArtist, arId))
if err:
return err
store.commit() store.commit()
return request.formatter({}) error = merge_errors(errors)
return request.formatter({ 'error': error }, error = True) if error else request.formatter({})
@app.route('/rest/setRating.view', methods = [ 'GET', 'POST' ]) @app.route('/rest/setRating.view', methods = [ 'GET', 'POST' ])
def rate(): def rate():