From 6a1f96167cf9cad6bf2e0ef7881b398e75b98169 Mon Sep 17 00:00:00 2001 From: Roberto Pastor Date: Tue, 25 Jun 2019 21:55:59 +0200 Subject: [PATCH] - Revamped getAPI, getVersion, getReadList, getIndex and getComic endpoints. - Normalized error responses - Normalized success responses for getVersion, getReadList, getIndex and getComic. --- mylar/api.py | 191 +++++++++++++++++++++++++++++---------------------- 1 file changed, 108 insertions(+), 83 deletions(-) diff --git a/mylar/api.py b/mylar/api.py index e8e534a5..58b45fec 100644 --- a/mylar/api.py +++ b/mylar/api.py @@ -38,9 +38,10 @@ cmd_list = ['getIndex', 'getComic', 'getUpcoming', 'getWanted', 'getHistory', 'getComicInfo', 'getIssueInfo', 'getArt', 'downloadIssue', 'downloadNZB', 'getReadList', 'getStoryArc', 'addStoryArc'] - class Api(object): + API_ERROR_CODE_DEFAULT = 460 + def __init__(self): self.apikey = None self.cmd = None @@ -54,25 +55,55 @@ class Api(object): self.apitype = None self.comicrn = False + def _failureResponse(self, errorMessage, code = API_ERROR_CODE_DEFAULT): + response = { + 'success': False, + 'error': { + 'code': code, + 'message': errorMessage + } + } + cherrypy.response.headers['Content-Type'] = "application/json" + return simplejson.dumps(response) + + def _successResponse(self, results): + response = { + 'success': True, + 'data': results + } + cherrypy.response.headers['Content-Type'] = "application/json" + return simplejson.dumps(response) + + def _resultsFromQuery(self, query): + myDB = db.DBConnection() + rows = myDB.select(query) + + results = [] + + for row in rows: + results.append(dict(zip(row.keys(), row))) + + return results + def checkParams(self, *args, **kwargs): if 'cmd' not in kwargs: - self.data = self._error_with_message('Missing parameter: cmd') + self.data = self._failureResponse('Missing parameter: cmd') return if 'apikey' not in kwargs and ('apikey' not in kwargs and kwargs['cmd'] != 'getAPI'): - self.data = self._error_with_message('Missing api key') + self.data = self._failureResponse('Missing API key') return elif kwargs['cmd'] == 'getAPI': self.apitype = 'normal' else: if not mylar.CONFIG.API_ENABLED: if kwargs['apikey'] != mylar.DOWNLOAD_APIKEY: - self.data = self._error_with_message('API not enabled') + self.data = self._failureResponse('API not enabled') return if kwargs['apikey'] != mylar.CONFIG.API_KEY and all([kwargs['apikey'] != mylar.DOWNLOAD_APIKEY, mylar.DOWNLOAD_APIKEY != None]): - self.data = self._error_with_message('Incorrect API key') + self.data = self._failureResponse('Incorrect API key') return else: if kwargs['apikey'] == mylar.CONFIG.API_KEY: @@ -83,22 +114,22 @@ class Api(object): self.apikey = kwargs.pop('apikey') if not([mylar.CONFIG.API_KEY, mylar.DOWNLOAD_APIKEY]): - self.data = self._error_with_message('API key not generated') + self.data = self._failureResponse('API key not generated') return if self.apitype: if self.apitype == 'normal' and len(mylar.CONFIG.API_KEY) != 32: - self.data = self._error_with_message('API key not generated correctly') + self.data = self._failureResponse('API key not generated correctly') return if self.apitype == 'download' and len(mylar.DOWNLOAD_APIKEY) != 32: - self.data = self._error_with_message('Download API key not generated correctly') + self.data = self._failureResponse('Download API key not generated correctly') return else: - self.data = self._error_with_message('API key not generated correctly') + self.data = self._failureResponse('API key not generated correctly') return if kwargs['cmd'] not in cmd_list: - self.data = self._error_with_message('Unknown command: %s' % kwargs['cmd']) + self.data = self._failureResponse('Unknown command: %s' % kwargs['cmd']) return else: self.cmd = kwargs.pop('cmd') @@ -179,38 +210,21 @@ class Api(object): ComicName as comicName\ FROM readlist' - def _dic_from_query(self, query): - myDB = db.DBConnection() - rows = myDB.select(query) - - rows_as_dic = [] - - for row in rows: - row_as_dic = dict(zip(row.keys(), row)) - rows_as_dic.append(row_as_dic) - - return rows_as_dic - - def _error_with_message(self, message): - error = {'error': {'message': message} } - cherrypy.response.headers['Content-Type'] = "application/json" - return simplejson.dumps(error) - def _getAPI(self, **kwargs): if 'username' not in kwargs: - self.data = self._error_with_message('Missing parameter: username') + self.data = self._failureResponse('Missing parameter: username') return else: username = kwargs['username'] if 'password' not in kwargs: - self.data = self._error_with_message('Missing parameter: password') + self.data = self._failureResponse('Missing parameter: password') return else: password = kwargs['password'] if any([mylar.CONFIG.HTTP_USERNAME is None, mylar.CONFIG.HTTP_PASSWORD is None]): - self.data = self._error_with_message('Unable to use this command - username & password MUST be enabled.') + self.data = self._failureResponse('Unable to use this command - username & password MUST be enabled.') return ht_user = mylar.CONFIG.HTTP_USERNAME @@ -218,38 +232,47 @@ class Api(object): ed_chk = edc.decrypt_it() if mylar.CONFIG.ENCRYPT_PASSWORDS is True: if username == ht_user and all([ed_chk['status'] is True, ed_chk['password'] == password]): - self.data = {'apikey': mylar.CONFIG.API_KEY} + self.data = self._successResponse( + {'apikey': mylar.CONFIG.API_KEY} + ) else: - self.data = self._error_with_message('Incorrect username or password.') + self.data = self._failureResponse('Incorrect username or password.') else: if username == ht_user and password == mylar.CONFIG.HTTP_PASSWORD: - self.data = {'apikey': mylar.CONFIG.API_KEY} + self.data = self._successResponse( + {'apikey': mylar.CONFIG.API_KEY} + ) else: - self.data = self._error_with_message('Incorrect username or password.') + self.data = self._failureResponse('Incorrect username or password.') def _getIndex(self, **kwargs): query = '{select} ORDER BY ComicSortName COLLATE NOCASE'.format( select = self._selectForComics() ) - - self.data = self._dic_from_query(query) + + self.data = self._successResponse( + self._resultsFromQuery(query) + ) return def _getReadList(self, **kwargs): - readListQuery ='{select} ORDER BY IssueDate ASC'.format( + readListQuery = '{select} ORDER BY IssueDate ASC'.format( select = self._selectForReadList() ) - self.data = self._dic_from_query(readListQuery) - + self.data = self._successResponse( + self._resultsFromQuery(readListQuery) + ) + return def _getComic(self, **kwargs): + if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -258,33 +281,35 @@ class Api(object): select = self._selectForComics(), id = self.id ) - comic = self._dic_from_query(comicQuery) + comic = self._resultsFromQuery(comicQuery) issuesQuery = '{select} WHERE ComicID="{id}" ORDER BY Int_IssueNumber DESC'.format( select = self._selectForIssues(), id = self.id ) - issues = self._dic_from_query(issuesQuery) + issues = self._resultsFromQuery(issuesQuery) if mylar.CONFIG.ANNUALS_ON: annualsQuery = '{select} WHERE ComicID="{id}"'.format( select = self._selectForAnnuals(), id = self.id ) - annuals = self._dic_from_query(annualsQuery) + annuals = self._resultsFromQuery(annualsQuery) else: annuals = [] - self.data = { + self.data = self._successResponse({ 'comic': comic, 'issues': issues, 'annuals': annuals - } + }) return def _getHistory(self, **kwargs): - self.data = self._dic_from_query('SELECT * from snatched order by DateAdded DESC') + self.data = self._successResponse( + self._resultsFromQuery('SELECT * from snatched order by DateAdded DESC') + ) return def _getUpcoming(self, **kwargs): @@ -304,14 +329,14 @@ class Api(object): week = today.strftime('%U') year = today.strftime('%Y') - self.data = self._dic_from_query( + self.data = self._resultsFromQuery( "SELECT w.COMIC AS ComicName, w.ISSUE AS IssueNumber, w.ComicID, w.IssueID, w.SHIPDATE AS IssueDate, w.STATUS AS Status, c.ComicName AS DisplayComicName \ FROM weekly w JOIN comics c ON w.ComicID = c.ComicID WHERE w.COMIC IS NOT NULL AND w.ISSUE IS NOT NULL AND \ SUBSTR('0' || w.weeknumber, -2) = '" + week + "' AND w.year = '" + year + "' AND " + select_status_clause + " ORDER BY c.ComicSortName") return def _getWanted(self, **kwargs): - self.data = self._dic_from_query("SELECT * from issues WHERE Status='Wanted'") + self.data = self._resultsFromQuery("SELECT * from issues WHERE Status='Wanted'") return def _getLogs(self, **kwargs): @@ -325,7 +350,7 @@ class Api(object): def _delComic(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -337,7 +362,7 @@ class Api(object): def _pauseComic(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -349,7 +374,7 @@ class Api(object): def _resumeComic(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -361,7 +386,7 @@ class Api(object): def _refreshComic(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -375,7 +400,7 @@ class Api(object): def _addComic(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -389,7 +414,7 @@ class Api(object): def _queueIssue(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -402,7 +427,7 @@ class Api(object): def _unqueueIssue(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -417,7 +442,7 @@ class Api(object): def _issueProcess(self, **kwargs): if 'comicid' not in kwargs: - self.data = self._error_with_message('Missing parameter: comicid') + self.data = self._failureResponse('Missing parameter: comicid') return else: self.comicid = kwargs['comicid'] @@ -428,7 +453,7 @@ class Api(object): self.issueid = kwargs['issueid'] if 'folder' not in kwargs: - self.data = self._error_with_message('Missing parameter: folder') + self.data = self._failureResponse('Missing parameter: folder') return else: self.folder = kwargs['folder'] @@ -441,13 +466,13 @@ class Api(object): def _forceProcess(self, **kwargs): if 'nzb_name' not in kwargs: - self.data = self._error_with_message('Missing parameter: nzb_name') + self.data = self._failureResponse('Missing parameter: nzb_name') return else: self.nzb_name = kwargs['nzb_name'] if 'nzb_folder' not in kwargs: - self.data = self._error_with_message('Missing parameter: nzb_folder') + self.data = self._failureResponse('Missing parameter: nzb_folder') return else: self.nzb_folder = kwargs['nzb_folder'] @@ -492,13 +517,13 @@ class Api(object): return def _getVersion(self, **kwargs): - self.data = { + self.data = self._successResponse({ 'git_path': mylar.CONFIG.GIT_PATH, 'install_type': mylar.INSTALL_TYPE, 'current_version': mylar.CURRENT_VERSION, 'latest_version': mylar.LATEST_VERSION, 'commits_behind': mylar.COMMITS_BEHIND, - } + }) def _checkGithub(self, **kwargs): versioncheck.checkGithub() @@ -515,7 +540,7 @@ class Api(object): def _getArtistArt(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -524,7 +549,7 @@ class Api(object): def _getIssueArt(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -533,7 +558,7 @@ class Api(object): def _getComicInfo(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -542,7 +567,7 @@ class Api(object): def _getIssueInfo(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -551,7 +576,7 @@ class Api(object): def _getArt(self, **kwargs): if 'id' not in kwargs: - self.data = self._error_with_message('Missing parameter: id') + self.data = self._failureResponse('Missing parameter: id') return else: self.id = kwargs['id'] @@ -567,7 +592,7 @@ class Api(object): return else: # If we cant find the image, lets check the db for a url. - comic = self._dic_from_query('SELECT * from comics WHERE ComicID="' + self.id + '"') + comic = self._resultsFromQuery('SELECT * from comics WHERE ComicID="' + self.id + '"') # Try every img url in the db try: @@ -586,9 +611,9 @@ class Api(object): self.img = image_path return else: - self.data = self._error_with_message('Failed return a image') + self.data = self._failureResponse('Failed return a image') else: - self.data = self._error_with_message('Failed to return a image') + self.data = self._failureResponse('Failed to return a image') def _findComic(self, name, issue=None, type_=None, mode=None, explisit=None, serinfo=None): # set defaults @@ -599,7 +624,7 @@ class Api(object): # Dont do shit if name is missing if len(name) == 0: - self.data = self._error_with_message('Missing a Comic name') + self.data = self._failureResponse('Missing a Comic name') return if type_ == 'comic' and mode == 'series': @@ -616,15 +641,15 @@ class Api(object): def _downloadIssue(self, id): if not id: - self.data = self._error_with_message('You need to provide a issueid') + self.data = self._failureResponse('You need to provide a issueid') return self.id = id # Fetch a list of dicts from issues table - i = self._dic_from_query('SELECT * from issues WHERE issueID="' + self.id + '"') + i = self._resultsFromQuery('SELECT * from issues WHERE issueID="' + self.id + '"') if not len(i): - self.data = self._error_with_message('Couldnt find a issue with issueID %s' % self.id) + self.data = self._failureResponse('Couldnt find a issue with issueID %s' % self.id) return # issueid is unique so it should one dict in the list @@ -635,7 +660,7 @@ class Api(object): # Check the issue is downloaded if issuelocation is not None: # Find the comic location - comic = self._dic_from_query('SELECT * from comics WHERE comicID="' + issue['ComicID'] + '"')[0] + comic = self._resultsFromQuery('SELECT * from comics WHERE comicID="' + issue['ComicID'] + '"')[0] comiclocation = comic.get('ComicLocation') f = os.path.join(comiclocation, issuelocation) if not os.path.isfile(f): @@ -648,12 +673,12 @@ class Api(object): self.file = f self.filename = issuelocation else: - self.data = self._error_with_message('You need to download that issue first') + self.data = self._failureResponse('You need to download that issue first') return def _downloadNZB(self, nzbname): if not nzbname: - self.data = self._error_with_message('You need to provide a nzbname') + self.data = self._failureResponse('You need to provide a nzbname') return self.nzbname = nzbname @@ -662,18 +687,18 @@ class Api(object): self.file = f self.filename = nzbname else: - self.data = self._error_with_message('NZBname does not exist within the cache directory. Unable to retrieve.') + self.data = self._failureResponse('NZBname does not exist within the cache directory. Unable to retrieve.') return def _getStoryArc(self, **kwargs): if not 'id' in kwargs: if 'customOnly' in kwargs and kwargs['customOnly']: - self.data = self._dic_from_query('SELECT StoryArcID, StoryArc, MAX(ReadingOrder) AS HighestOrder from storyarcs WHERE StoryArcID LIKE "C%" GROUP BY StoryArcID ORDER BY StoryArc') + self.data = self._resultsFromQuery('SELECT StoryArcID, StoryArc, MAX(ReadingOrder) AS HighestOrder from storyarcs WHERE StoryArcID LIKE "C%" GROUP BY StoryArcID ORDER BY StoryArc') else: - self.data = self._dic_from_query('SELECT StoryArcID, StoryArc, MAX(ReadingOrder) AS HighestOrder from storyarcs GROUP BY StoryArcID ORDER BY StoryArc') + self.data = self._resultsFromQuery('SELECT StoryArcID, StoryArc, MAX(ReadingOrder) AS HighestOrder from storyarcs GROUP BY StoryArcID ORDER BY StoryArc') else: self.id = kwargs['id'] - self.data = self._dic_from_query('SELECT StoryArc, ReadingOrder, ComicID, ComicName, IssueNumber, IssueID, \ + self.data = self._resultsFromQuery('SELECT StoryArc, ReadingOrder, ComicID, ComicName, IssueNumber, IssueID, \ IssueDate, IssueName, IssuePublisher from storyarcs WHERE StoryArcID="' + self.id + '" ORDER BY ReadingOrder') return @@ -682,17 +707,17 @@ class Api(object): if not 'id' in kwargs: self.id = 'C%04d' % random.randint(1, 9999) if not 'storyarcname' in kwargs: - self.data = self._error_with_message('You need to provide either id or storyarcname') + self.data = self._failureResponse('You need to provide either id or storyarcname') return else: storyarcname = kwargs.pop('storyarcname') else: self.id = kwargs.pop('id') - arc = self._dic_from_query('SELECT * from storyarcs WHERE StoryArcID="' + self.id + '" ORDER by ReadingOrder') + arc = self._resultsFromQuery('SELECT * from storyarcs WHERE StoryArcID="' + self.id + '" ORDER by ReadingOrder') storyarcname = arc[0]['StoryArc'] issuecount = len(arc) if not 'issues' in kwargs and not 'arclist' in kwargs: - self.data = self._error_with_message('No issues specified') + self.data = self._failureResponse('No issues specified') return else: arclist = ""