diff --git a/hc/api/models.py b/hc/api/models.py index 5271d911..851efcda 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -778,6 +778,9 @@ class Flip(models.Model): condition=models.Q(processed=None), ) ] + + def to_dict(self): + return {"timestamp": self.created, "up": 1 if self.new_status == "up" else 0} def send_alerts(self): if self.new_status == "up" and self.old_status in ("new", "paused"): diff --git a/hc/api/tests/test_get_flips.py b/hc/api/tests/test_get_flips.py new file mode 100644 index 00000000..e6e5aa17 --- /dev/null +++ b/hc/api/tests/test_get_flips.py @@ -0,0 +1,56 @@ +from datetime import timedelta as td +from datetime import datetime as dt +from datetime import timezone + +from hc.api.models import Check, Flip +from hc.test import BaseTestCase + + +class GetFlipsTestCase(BaseTestCase): + def setUp(self): + super(GetFlipsTestCase, self).setUp() + + self.a1 = Check(project=self.project, name="Alice 1") + self.a1.timeout = td(seconds=3600) + self.a1.grace = td(seconds=900) + self.a1.n_pings = 0 + self.a1.status = "new" + self.a1.tags = "a1-tag a1-additional-tag" + self.a1.desc = "This is description" + self.a1.save() + + self.url = "/api/v1/checks/%s/flips/" % self.a1.code + + def get(self, api_key="X" * 32): + return self.client.get(self.url, HTTP_X_API_KEY=api_key) + + def test_it_works(self): + self.f1 = Flip( + owner=self.a1, + created=dt(2020,6,1,12,24,32,tzinfo=timezone.utc), + old_status='new', + new_status='up', + ) + self.f1.save() + + r = self.get() + self.assertEqual(r.status_code, 200) + self.assertEqual(r["Access-Control-Allow-Origin"], "*") + + doc = r.json() + self.assertEqual(len(doc["flips"]), 1) + + flip = doc["flips"][0] + self.assertEqual(flip["timestamp"], dt(2020,6,1,12,24,32,tzinfo=timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')) + self.assertEqual(flip["up"], 1) + + def test_readonly_key_is_allowed(self): + self.project.api_key_readonly = "R" * 32 + self.project.save() + + r = self.get(api_key=self.project.api_key_readonly) + self.assertEqual(r.status_code, 200) + + def test_it_rejects_post(self): + r = self.client.post(self.url, HTTP_X_API_KEY="X" * 32) + self.assertEqual(r.status_code, 405) diff --git a/hc/api/urls.py b/hc/api/urls.py index d978e35f..09be5cfb 100644 --- a/hc/api/urls.py +++ b/hc/api/urls.py @@ -38,8 +38,8 @@ urlpatterns = [ path("api/v1/checks//pause", views.pause, name="hc-api-pause"), path("api/v1/notifications//bounce", views.bounce, name="hc-api-bounce"), path("api/v1/checks//pings/", views.pings, name="hc-api-pings"), - path("api/v1/checks//flips/", views.flips, name="hc-api-flips"), - path("api/v1/checks//flips/", views.get_flips_by_unique_key), + path("api/v1/checks//flips/", views.flips_by_uuid, name="hc-api-flips"), + path("api/v1/checks//flips/", views.flips_by_unique_key), path("api/v1/channels/", views.channels), path( "badge///.", diff --git a/hc/api/views.py b/hc/api/views.py index 67655050..488859ab 100644 --- a/hc/api/views.py +++ b/hc/api/views.py @@ -190,9 +190,6 @@ def get_check(request, code): check = get_object_or_404(Check, code=code) if check.project_id != request.project.id: return HttpResponseForbidden() - - if 'history' in request.GET: - return JsonResponse(check.to_dict(readonly=request.readonly, history=request.GET['history'])) return JsonResponse(check.to_dict(readonly=request.readonly)) @@ -295,60 +292,52 @@ def pings(request, code): return JsonResponse({"pings": dicts}) -@cors("GET") -@csrf_exempt -@validate_json() -@authorize_read -def flips(request, code): - check = get_object_or_404(Check, code=code) +def flips(request, check): if check.project_id != request.project.id: return HttpResponseForbidden() if all(x not in request.GET for x in ('start','end','seconds')): - flips = Flip.objects.select_related("owner").filter( + flips = Flip.objects.filter( owner=check, new_status__in=("down","up"), ).order_by("created") else: - if any(x in request.GET for x in ('start','end')) and 'seconds' in request.GET: + if "seconds" in request.GET and ("start" in request.GET or "end" in request.GET): return HttpResponseBadRequest() - history_start = None - history_end = datetime.now() + flips = Flip.objects.filter( + owner=check, new_status__in=("down","up")) if 'start' in request.GET: - history_start = datetime.fromtimestamp(int(request.GET['start'])) + flips = flips.filter(created_gt=datetime.fromtimestamp(int(request.GET['start']))) + if 'end' in request.GET: - history_end = datetime.fromtimestamp(int(request.GET['end'])) + flips = flips.filter(created__lt=datetime.fromtimestamp(int(request.GET['end']))) if 'seconds' in request.GET: - history_start = datetime.now()-td(seconds=int(request.GET['seconds'])) - - flips = Flip.objects.select_related("owner").filter( - owner=check, new_status__in=("down","up"), - created__gt=history_start, - created__lt=history_end - ).order_by("created") + flips = flips.filter(created_gt=datetime.now()-td(seconds=int(request.GET['seconds']))) - dictStatus = {"up":1,"down":0} + flips = flips.order_by("created") + - return JsonResponse({"flips": list(map(lambda x: {'timestamp':x.created,'up':dictStatus[x.new_status]}, flips))}) + return JsonResponse({"flips": [flip.to_dict() for flip in flips]}) - # return JsonResponse(check.to_dict( - # readonly=request.readonly, - # history=( - # history_start,history_end - # ) - # )) +@cors("GET") +@csrf_exempt +@validate_json() +@authorize_read +def flips_by_uuid(request,code): + check = get_object_or_404(Check, code=code) + return flips(request,check) @cors("GET") @csrf_exempt @validate_json() @authorize_read -def get_flips_by_unique_key(request, unique_key): +def flips_by_unique_key(request, unique_key): checks = Check.objects.filter(project=request.project.id) for check in checks: if check.unique_key == unique_key: - return flips(request,check.code) + return flips(request,check) return HttpResponseNotFound() @never_cache