From 014edeca57deed3141dda9056949e537f55fdf25 Mon Sep 17 00:00:00 2001 From: Thierry Parmentelat Date: Fri, 17 May 2013 13:21:33 +0200 Subject: [PATCH] ManifoldAPI raises ManifoldException if anything goes wrong SliceView catches this at the highest level (and logs out user) ManifoldProxy catches this too and forwards manifoldresult to js --- manifold/manifoldapi.py | 52 +++++++++++++++++++++++--------------- manifold/manifoldproxy.py | 12 ++++++--- manifold/manifoldresult.py | 13 ++++++++++ manifold/metadata.py | 13 ++++------ trash/sliceview.py | 28 ++++++++++++++++---- unfold/page.py | 2 +- 6 files changed, 82 insertions(+), 38 deletions(-) diff --git a/manifold/manifoldapi.py b/manifold/manifoldapi.py index 2548287f..14eef3be 100644 --- a/manifold/manifoldapi.py +++ b/manifold/manifoldapi.py @@ -3,7 +3,7 @@ import xmlrpclib from myslice.config import Config -from manifoldresult import ManifoldResult, ManifoldCode +from manifoldresult import ManifoldResult, ManifoldCode, ManifoldException debug=False debug=True @@ -24,6 +24,13 @@ class ManifoldAPI: def __repr__ (self): return "ManifoldAPI[%s]"%self.url + # a one-liner to give a hint of what the return value looks like + def _print_result (self, result): + if not result: print "[no/empty result]" + elif isinstance (result,str): print "result is '%s'"%result + elif isinstance (result,list): print "result is a %d-elts list"%len(result) + else: print "[dont know how to display result]" + # xxx temporary code for scaffolding a ManifolResult on top of an API that does not expose error info # as of march 2013 we work with an API that essentially either returns the value, or raises # an xmlrpclib.Fault exception with always the same 8002 code @@ -32,29 +39,30 @@ class ManifoldAPI: # a SESSION_EXPIRED code def __getattr__(self, methodName): def func(*args, **kwds): - if (debug): - print "entering ManifoldAPI.%s"%methodName, - print "args",args, - print "kwds",kwds try: + if debug: print "====> ManifoldAPI.%s"%methodName,"args",args,"kwds",kwds result=getattr(self.server, methodName)(self.auth, *args, **kwds) + if debug: + print '<==== backend call %s(*%s,**%s) returned'%(methodName,args,kwds), + print '.ctd. Authmethod=',self.auth['AuthMethod'], self.url,'->', + self._print_result(result) ### attempt to cope with old APIs and new APIs if isinstance (result, dict) and 'code' in result: - # this sounds like a result from a new API, leave it untouched - # XXX jordan : we need to wrap it into a ResultValue structure - # XXX this is not good until we merge both repos - if result['code'] != 2: + if debug: print "taken as new API" + # this sounds like a result from a new API + # minimal treatment is required, but we do want to turn this into a + # class instance + if result['code'] != 2: # in the manifold world, this can be either + # 0 (ok) 1 (partial result) or 2 which means error + if debug: print "OK (new API)" return ManifoldResult(code=result['code'], value=result['value']) else: - return ManifoldResult(code=result['code'], output=result['description']) + if debug: print "KO (new API) - raising ManifoldException" + raise ManifoldException(ManifoldResult(code=result['code'], output=result['description'])) else: - if debug: - print '<=== backend call', methodName, args, kwds - print '.... ctd', 'Authmethod=',self.auth['AuthMethod'], self.url,'->', - if not result: print "[no/empty result]" - elif isinstance (result,str): print "result is '%s'"%result - elif isinstance (result,list): print "result is a %d-elts list"%len(result) - else: print "[dont know how to display result]" + if debug: print "taken as old API" + # we're talking to an old API + # so if we make it here it should mean success return ManifoldResult (code=ManifoldCode.SUCCESS, value=result) except xmlrpclib.Fault, error: ### xxx this is very rough for now @@ -62,15 +70,17 @@ class ManifoldAPI: # in some less unpolite way than this anonymous exception, we assume it's a problem with the session # that needs to be refreshed if error.faultCode == 8002: + if debug: print "KO (old API - 8002) - raising ManifoldException" reason="most likely your session has expired" reason += " (the manifold API has no unambiguous error reporting mechanism yet)" - return ManifoldResult (code=ManifoldCode.SESSION_EXPIRED, output=reason) + raise ManifoldException ( ManifoldResult (code=ManifoldCode.SESSION_EXPIRED, output=reason)) else: + if debug: print "KO (old API - other) - raising ManifoldException" reason="xmlrpclib.Fault with faultCode = %s (not taken as session expired)"%error.faultCode - return ManifoldResult (code=ManifoldCode.UNKNOWN_ERROR, output=reason) + raise ManifoldException ( ManifoldResult (code=ManifoldCode.UNKNOWN_ERROR, output=reason)) except Exception,error: - print "ManifoldAPI: unexpected exception",error - return ManifoldResult (code=ManifoldCode.UNKNOWN_ERROR, output="%s"%error) + if debug: print "KO (unexpected exception)",error + raise ManifoldException ( ManifoldResult (code=ManifoldCode.UNKNOWN_ERROR, output="%s"%error) ) return func def send_manifold_query (self, query): diff --git a/manifold/manifoldproxy.py b/manifold/manifoldproxy.py index 11829557..99de7849 100644 --- a/manifold/manifoldproxy.py +++ b/manifold/manifoldproxy.py @@ -6,6 +6,7 @@ from django.http import HttpResponse, HttpResponseForbidden #from manifold.manifoldquery import ManifoldQuery from manifold.core.query import Query from manifold.manifoldapi import ManifoldAPI +from manifold.manifoldresult import ManifoldException debug=False debug=True @@ -73,8 +74,13 @@ with the query passed using POST""" # actually forward manifold_api= ManifoldAPI(auth=manifold_api_session_auth) - if debug: print 'manifoldproxy.proxy: sending to backend', manifold_query - answer=manifold_api.send_manifold_query (manifold_query) + if debug: print '===> manifoldproxy.proxy: sending to backend', manifold_query + # for the benefit of the python code, manifoldAPI raises an exception if something is wrong + # however in this case we want to propagate the complete manifold result to the js world + try: + answer=manifold_api.send_manifold_query (manifold_query) + except ManifoldException, manifold_result: + answer=manifold_result print "="*80 print "ANSWER IN PROXY", answer print answer.ok_value() @@ -89,7 +95,7 @@ with the query passed using POST""" else: print "result is other (type=%s) : %s"%(type(value),value) json_answer=json.dumps(answer) # if in debug mode we save this so we can use offline mode later - if (debug): + if debug: with (file(offline_filename,"w")) as f: f.write(json_answer) # this is an artificial delay added for debugging purposes only diff --git a/manifold/manifoldresult.py b/manifold/manifoldresult.py index 1c812f0c..a16a9a73 100644 --- a/manifold/manifoldresult.py +++ b/manifold/manifoldresult.py @@ -21,11 +21,16 @@ class ManifoldResult (dict): for k in ['code','value','output']: self[k]=d[k] + # raw accessors + def code (self): return self['code'] + def output (self): return self['output'] + # this returns None if there's a problem, the value otherwise def ok_value (self): if self['code']==ManifoldCode.SUCCESS: return self['value'] + # both data in a single string def error (self): return "code=%s -- %s"%(self['code'],self['output']) @@ -40,3 +45,11 @@ class ManifoldResult (dict): result += " [output=%s]"%self['output'] result += "]]" return result + +# probably simpler to use a single class and transport the whole result there +# instead of a clumsy set of derived classes +class ManifoldException (Exception): + def __init__ (self, manifold_result): + self.manifold_result=manifold_result + def __repr__ (self): + return "Manifold Exception %s"%(self.manifold_result.error()) diff --git a/manifold/metadata.py b/manifold/metadata.py index 0924ded0..59d600b1 100644 --- a/manifold/metadata.py +++ b/manifold/metadata.py @@ -16,9 +16,6 @@ class MetaData: self.auth=auth self.hash_by_object={} - # XXX Retrieve all metadata the first time we instanciate the class - self.fetch() - def fetch (self): offline_filename="offline-metadata.json" if work_offline: @@ -39,14 +36,14 @@ class MetaData: 'object': 'local:object', # proposed to replace metadata:table 'fields': fields }) -#old# rows_result = manifold_api.Get('metadata:table', [], [], fields) rows = rows_result.ok_value() - if not rows: - print "Failed to retrieve metadata",rows_result.error() - rows=[] +# API errors will be handled by the outer logic +# if not rows: +# print "Failed to retrieve metadata",rows_result.error() +# rows=[] self.hash_by_object = dict ( [ (row['table'], row) for row in rows ] ) # save for next time we use offline mode - if debug: + if debug and rows: with file(offline_filename,'w') as f: f.write(json.dumps(self.hash_by_object)) diff --git a/trash/sliceview.py b/trash/sliceview.py index fcbaac77..9865f774 100644 --- a/trash/sliceview.py +++ b/trash/sliceview.py @@ -4,6 +4,7 @@ from django.template import RequestContext from django.shortcuts import render_to_response from django.contrib.auth.decorators import login_required +from django.http import HttpResponseRedirect from unfold.page import Page #from manifold.manifoldquery import ManifoldQuery @@ -20,8 +21,9 @@ from plugins.querycode.querycode import QueryCode from plugins.quickfilter.quickfilter import QuickFilter from plugins.messages.messages import Messages -from myslice.viewutils import quickfilter_criterias +from manifold.manifoldresult import ManifoldException +from myslice.viewutils import quickfilter_criterias from myslice.viewutils import topmenu_items, the_user # XXX JORDAN @@ -32,18 +34,34 @@ debug = True @login_required def slice_view (request, slicename=tmp_default_slice): - + # xxx Thierry - ugly hack + # fetching metadata here might fail - e.g. with an expired session.. + # let's catch this early on and log out our user if needed + # it should of course be handled in a more generic way + try: + return _slice_view(request,slicename) + except ManifoldException, manifold_result: + # xxx needs a means to display this message to user... + from django.contrib.auth import logout + logout(request) + return HttpResponseRedirect ('/') + except Exception, e: + # xxx we need to sugarcoat this error message in some error template... + print "Unexpected exception",e + # return ... + +def _slice_view (request, slicename): + page = Page(request) page.expose_js_metadata() - # TODO The query to run is embedded in the URL main_query = Query.get('slice').filter_by('slice_hrn', '=', slicename) # Get default fields from metadata unless specified if not main_query.fields: - md_fields = page.get_metadata() - md_fields = md_fields.details_by_object('slice') + metadata = page.get_metadata() + md_fields = metadata.details_by_object('slice') if debug: print "METADATA", md_fields # TODO Get default fields diff --git a/unfold/page.py b/unfold/page.py index b49dce12..a97ec6dc 100644 --- a/unfold/page.py +++ b/unfold/page.py @@ -105,7 +105,7 @@ class Page: manifold_api_session_auth = session['manifold']['auth'] metadata=MetaData (manifold_api_session_auth) metadata.fetch() - # store it for next time + # store it for next time manifold['metadata']=metadata if debug: print "Page.get_metadata: return new value" return metadata -- 2.43.0