from django.shortcuts import get_object_or_404 from django.http import HttpResponse, Http404, HttpResponseForbidden from django.conf import settings from django.views.decorators.csrf import csrf_exempt from functools import wraps from django.utils.decorators import available_attrs from django.db import connection import datetime import json from models import CommitFest, Patch, PatchBuildStatus, BuildProvider def api_authenticate(view_func): def _wrapped_view(request, *args, **kwargs): if not request.META['REMOTE_ADDR'] in settings.API_AUTH: return HttpResponseForbidden('Access Denied') if not request.META.get('HTTP_X_API_AUTH', '') == settings.API_AUTH[request.META['REMOTE_ADDR']]: return HttpResponseForbidden('Access Denied') return view_func(request, *args, **kwargs) return wraps(view_func, assigned=available_attrs(view_func))(_wrapped_view) @api_authenticate def active_commitfests(request): cfs = list(CommitFest.objects.filter(status=CommitFest.STATUS_INPROGRESS)) if not cfs: cfs = CommitFest.objects.filter(status=CommitFest.STATUS_OPEN).order_by('-id')[:1] res = [ {'id': c.id, 'name': c.name, 'status': c.statusstring, 'numstatus': c.status} for c in cfs ] return HttpResponse(json.dumps(res), content_type='application/json') @api_authenticate def commitfest(request, cfid): cf = get_object_or_404(CommitFest, pk=cfid) whereclauses = [] params = { 'cid': cf.id } if 'since' in request.GET: whereclauses.append("latestmessage > %(since)s") params['since'] = request.GET['since'] if whereclauses: wherestring = 'AND ({0})'.format( ' AND '.join(whereclauses) ) else: wherestring = '' curs = connection.cursor() curs.execute("""SELECT p.id, p.name, poc.status, json_agg(json_build_object( 'msgid', mt.messageid, 'first', mt.firstmessage, 'latest', mt.latestmessage, 'attachments', attachments )) AS threads FROM commitfest_patch p INNER JOIN commitfest_patchoncommitfest poc ON poc.patch_id=p.id LEFT JOIN ( SELECT mtp.patch_id, t.messageid, t.firstmessage, t.latestmessage, json_agg(json_build_object('messageid', mta.messageid, 'attachmentid', mta.attachmentid, 'filename', mta.filename, 'time', mta.date)) AS attachments FROM commitfest_mailthread_patches mtp INNER JOIN commitfest_mailthread t ON t.id=mtp.mailthread_id LEFT JOIN commitfest_mailthreadattachment mta ON mta.mailthread_id=t.id GROUP BY mtp.patch_id, t.id ) AS mt ON mt.patch_id=p.id WHERE poc.commitfest_id=%(cid)s {0} GROUP BY p.id, poc.id""".format(wherestring), params) res = { 'patches': [dict(zip([col[0] for col in curs.description], row)) for row in curs.fetchall()], } return HttpResponse(json.dumps(res), content_type='application/json') @csrf_exempt @api_authenticate def build_result(request, cfid, patchid): if request.method != 'POST': return HttpResponse('Invalid method', status=405) if request.META['CONTENT_TYPE'] != 'application/json': return HttpResponse("Only JSON accepted", status=415) try: obj = json.loads(request.body) except ValueError: return HttpResponse("Invalid data format", status=415) commitfest = get_object_or_404(CommitFest, pk=cfid) patch = get_object_or_404(Patch, pk=patchid) # Mandatory fields try: provider = BuildProvider.objects.get(urlname=obj['provider']) messageid = obj['messageid'] status = obj['status'] statustime = obj['timestamp'] except BuildProvider.DoesNotExist: return HttpResponse("Invalid build provider", status=422) except KeyError, e: return HttpResponse("Mandatory parameter {0} missing".format(e.args[0]), status=400) if not status in PatchBuildStatus._STATUS_MAP: return HttpResponse("Invalid build status {0}".format(status), status=422) try: statustime = datetime.datetime.strptime(statustime, '%Y-%m-%dT%H:%M:%S.%fZ') except ValueError: return HttpResponse("Invalid timestamp {0}".format(statustime), status=422) # Optional parameters url = obj.get('url', '') commitid = obj.get('commit', '') (buildstatus, created) = PatchBuildStatus.objects.get_or_create(commitfest=commitfest, patch=patch, buildprovider=provider, status_timestamp=statustime, defaults={ 'buildmessageid': messageid, 'buildstatus': PatchBuildStatus._STATUS_MAP[status], 'status_url': url, 'master_commit_id': commitid, }, ) if not created: if buildstatus.buildmessageid == messageid and \ buildstatus.buildstatus == PatchBuildStatus._STATUS_MAP[status] and \ buildstatus.status_url == url and \ buildstatus.master_commit_id == commitid: return HttpResponse("Build status already stored", status=200) return HttpResponse("Conflicting build status already stored", status=409) # That's it! return HttpResponse("Stored", status=201)