From 751e9182ac7f37506c3d85cade00ef6f3986eb7a Mon Sep 17 00:00:00 2001 From: Alexandru DAMIAN Date: Tue, 2 Jun 2015 12:23:12 +0100 Subject: bitbake: convert all project-based view to JSON APIs This patch converts all-project views that are REST-style URLs to support JSON encoding if the "format=json" parameter is supplied. The formatting code is enhanced to prevent following Django foreign keys, to convert enum-values from int to string, and support for timedelta. (Bitbake rev: 7a6eb36b82c5f2e342777e479d9c401ded42453d) Signed-off-by: Alexandru DAMIAN Signed-off-by: Richard Purdie --- bitbake/lib/toaster/toastergui/views.py | 164 +++++++++++++++++++------------- 1 file changed, 99 insertions(+), 65 deletions(-) (limited to 'bitbake/lib') diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py index d4a9b4ca0d..f2626f8521 100755 --- a/bitbake/lib/toaster/toastergui/views.py +++ b/bitbake/lib/toaster/toastergui/views.py @@ -87,6 +87,55 @@ def _project_recent_build_list(prj): list(prj.buildrequest_set.filter(state__in=[BuildRequest.REQ_COMPLETED, BuildRequest.REQ_FAILED]).order_by("-pk")[:3])) +def _template_renderer(template): + def func_wrapper(view): + def returned_wrapper(request, *args, **kwargs): + try: + context = view(request, *args, **kwargs) + except RedirectException as e: + return e.get_redirect_response() + + if request.GET.get('format', None) == 'json': + # objects is a special keyword - it's a Page, but we need the actual objects here + # in XHR, the objects come in the "list" property + if "objects" in context: + context["list"] = context["objects"].object_list + del context["objects"] + + # we're about to return; to keep up with the XHR API, we set the error to OK + context["error"] = "ok" + def _objtojson(obj): + from django.db.models.query import QuerySet + from django.db.models import Model, IntegerField + if isinstance(obj, datetime): + return obj.isoformat() + elif isinstance(obj, timedelta): + return obj.total_seconds() + elif isinstance(obj, QuerySet) or isinstance(obj, set): + return list(obj) + elif hasattr( obj, '__dict__'): + d = obj.__dict__ + nd = dict(d) + for di in d: + if di.startswith("_"): + del nd[di] + elif isinstance(d[di], Model): + nd[di] = d[di].pk + elif isinstance(d[di], int) and hasattr(obj, "get_%s_display" % di): + nd[di] = getattr(obj, "get_%s_display" % di)() + return nd + else: + raise TypeError("Unserializable object %s of type %s" % ( obj, type(obj))) + + return HttpResponse(jsonfilter(context, default=_objtojson ), + content_type = "application/json; charset=utf-8") + else: + return render(request, template, context) + return returned_wrapper + return func_wrapper + + + def _build_page_range(paginator, index = 1): try: page = paginator.page(index) @@ -129,7 +178,7 @@ def _redirect_parameters(view, g, mandatory_parameters, *args, **kwargs): if not i in params: params[i] = urllib.unquote(str(mandatory_parameters[i])) - return redirect(url + "?%s" % urllib.urlencode(params), permanent = False, *args, **kwargs) + return redirect(url + "?%s" % urllib.urlencode(params), permanent = False, **kwargs) class RedirectException(Exception): def __init__(self, view, g, mandatory_parameters, *args, **kwargs): @@ -141,7 +190,7 @@ class RedirectException(Exception): self.okwargs = kwargs def get_redirect_response(self): - return _redirect_parameters(self.view, self.g, self.mandatory_parameters, self.oargs, self.okwargs) + return _redirect_parameters(self.view, self.g, self.mandatory_parameters, self.oargs, **self.okwargs) FIELD_SEPARATOR = ":" AND_VALUE_SEPARATOR = "!" @@ -1839,8 +1888,8 @@ if toastermain.settings.MANAGED: # shows the "all builds" page for managed mode; it displays build requests (at least started!) instead of actual builds + @_template_renderer("managed_builds.html") def builds(request): - template = 'managed_builds.html' # define here what parameters the view needs in the GET portion in order to # be able to display something. 'count' and 'page' are mandatory for all views # that use paginators. @@ -1850,12 +1899,10 @@ if toastermain.settings.MANAGED: try: context, pagesize, orderby = _build_list_helper(request, buildrequests, True) except InvalidRequestException as e: - return _redirect_parameters( builds, request.GET, e.response) + raise RedirectException( builds, request.GET, e.response) - response = render(request, template, context) _set_parameters_values(pagesize, orderby, request) - return response - + return context # helper function, to be used on "all builds" and "project builds" pages @@ -2151,8 +2198,8 @@ if toastermain.settings.MANAGED: # Shows the edit project page + @_template_renderer('project.html') def project(request, pid): - template = "project.html" try: prj = Project.objects.get(id = pid) except Project.DoesNotExist: @@ -2207,10 +2254,7 @@ if toastermain.settings.MANAGED: except ProjectVariable.DoesNotExist: context["distro"] = "-- not set yet" - response = render(request, template, context) - response['Cache-Control'] = "no-cache, must-revalidate, no-store" - response['Pragma'] = "no-cache" - return response + return context def xhr_projectbuild(request, pid): @@ -2680,8 +2724,8 @@ if toastermain.settings.MANAGED: return(vars_managed,sorted(vars_fstypes),vars_blacklist) + @_template_renderer("projectconf.html") def projectconf(request, pid): - template = "projectconf.html" try: prj = Project.objects.get(id = pid) @@ -2730,21 +2774,20 @@ if toastermain.settings.MANAGED: except ProjectVariable.DoesNotExist: pass - return render(request, template, context) + return context + @_template_renderer('projectbuilds.html') def projectbuilds(request, pid): - template = 'projectbuilds.html' buildrequests = BuildRequest.objects.filter(project_id = pid).exclude(state__lte = BuildRequest.REQ_INPROGRESS).exclude(state=BuildRequest.REQ_DELETED) try: context, pagesize, orderby = _build_list_helper(request, buildrequests, False) except InvalidRequestException as e: - return _redirect_parameters(projectbuilds, request.GET, e.response, pid = pid) + raise RedirectException('projectbuilds', request.GET, e.response, pid = pid) - response = render(request, template, context) _set_parameters_values(pagesize, orderby, request) - return response + return context def _file_name_for_artifact(b, artifact_type, artifact_id): @@ -2861,32 +2904,8 @@ if toastermain.settings.MANAGED: return build_mru - def template_renderer(template): - def func_wrapper(view): - def returned_wrapper(request, *args, **kwargs): - try: - context = view(request, *args, **kwargs) - except RedirectException as e: - return e.get_redirect_response() - - if request.GET.get('format', None) == 'json': - # objects is a special keyword - it's a Page, but we need the actual objects here - # in XHR, the objects come in the "list" property - if "objects" in context: - context["list"] = context["objects"].object_list - del context["objects"] - - # we're about to return; to keep up with the XHR API, we set the error to OK - context["error"] = "ok" - - return HttpResponse(jsonfilter(context, default=lambda obj: obj.isoformat() if isinstance(obj, datetime) else obj.__dict__ ), - content_type = "application/json; charset=utf-8") - else: - return render(request, template, context) - return returned_wrapper - return func_wrapper - @template_renderer("projects.html") + @_template_renderer("projects.html") def projects(request): (pagesize, orderby) = _get_parameters_values(request, 10, 'updated:-') mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby } @@ -2995,18 +3014,19 @@ if toastermain.settings.MANAGED: _set_parameters_values(pagesize, orderby, request) return context + @_template_renderer("buildrequestdetails.html") def buildrequestdetails(request, pid, brid): - template = "buildrequestdetails.html" context = { 'buildrequest' : BuildRequest.objects.get(pk = brid, project_id = pid) } - return render(request, template, context) + return context else: # shows the "all builds" page for interactive mode; this is the old code, simply moved + + @_template_renderer('build.html') def builds(request): - template = 'build.html' # define here what parameters the view needs in the GET portion in order to # be able to display something. 'count' and 'page' are mandatory for all views # that use paginators. @@ -3014,7 +3034,7 @@ else: mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby } retval = _verify_parameters( request.GET, mandatory_parameters ) if retval: - return _redirect_parameters( 'all-builds', request.GET, mandatory_parameters) + raise RedirectException( 'all-builds', request.GET, mandatory_parameters) # boilerplate code that takes a request for an object type and returns a queryset # for that object type. copypasta for all needed table searches @@ -3194,55 +3214,69 @@ else: # merge daterange values context.update(context_date) - - response = render(request, template, context) _set_parameters_values(pagesize, orderby, request) - return response + + return context + @_template_renderer('landing_not_managed.html') def newproject(request): - return render(request, 'landing_not_managed.html') + return {} + @_template_renderer('landing_not_managed.html') def project(request, pid): - return render(request, 'landing_not_managed.html') + return {} + @_template_renderer('landing_not_managed.html') def xhr_projectbuild(request, pid): - return render(request, 'landing_not_managed.html') + return {} + @_template_renderer('landing_not_managed.html') def xhr_projectinfo(request): - return render(request, 'landing_not_managed.html') + return {} + @_template_renderer('landing_not_managed.html') def xhr_projectedit(request, pid): - return render(request, 'landing_not_managed.html') + return {} + @_template_renderer('landing_not_managed.html') def xhr_datatypeahead(request): - return render(request, 'landing_not_managed.html') + return {} + @_template_renderer('landing_not_managed.html') def xhr_configvaredit(request, pid): - return render(request, 'landing_not_managed.html') + return {} + @_template_renderer('landing_not_managed.html') def importlayer(request): - return render(request, 'landing_not_managed.html') + return {} + @_template_renderer('landing_not_managed.html') def projectconf(request, pid): - return render(request, 'landing_not_managed.html') + return {} + @_template_renderer('landing_not_managed.html') def projectbuilds(request, pid): - return render(request, 'landing_not_managed.html') + return {} + @_template_renderer('landing_not_managed.html') def build_artifact(request, build_id, artifact_type, artifact_id): - return render(request, 'landing_not_managed.html') + return {} + @_template_renderer('landing_not_managed.html') def projects(request): - return render(request, 'landing_not_managed.html') + return {} + @_template_renderer('landing_not_managed.html') def xhr_importlayer(request): - return render(request, 'landing_not_managed.html') + return {} + @_template_renderer('landing_not_managed.html') def xhr_updatelayer(request): - return render(request, 'landing_not_managed.html') + return {} + @_template_renderer('landing_not_managed.html') def buildrequestdetails(request, pid, brid): - return render(request, 'landing_not_managed.html') + return {} -- cgit v1.2.3-54-g00ecf