diff --git a/hc/api/models.py b/hc/api/models.py index a6f7d462..c4b9f6e3 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -207,7 +207,7 @@ class Check(models.Model): code_half = self.code.hex[:16] return hashlib.sha1(code_half.encode()).hexdigest() - def to_dict(self, readonly=False, history=None): + def to_dict(self, readonly=False): result = { "name": self.name, @@ -224,24 +224,6 @@ class Check(models.Model): if self.last_duration: result["last_duration"] = int(self.last_duration.total_seconds()) - if history: - split = re.split(r'(h|d|w)$',history,maxsplit=0) - if len(split) == 3: # re.split should return a list of 3 items if the parameter is set correctly - zone = pytz.timezone(self.tz) - current_now = datetime.now(tz=zone) - - if split[1] == 'd': - cutoff = current_now - td(days=int(split[0])) - elif split[1] == 'h': - cutoff = current_now - td(hours=int(split[0])) - elif split[1] == 'w': - cutoff = current_now - td(weeks=int(split[0])) - - flips = Flip.objects.select_related("owner").filter( - owner=self, new_status__in=("down","up"), created__gt=cutoff - ).order_by("created") - dictStatus = {"up":1,"down":0} - result['history'] = list(map(lambda x: {'timestamp':x.created,'up':dictStatus[x.new_status]}, flips)) if readonly: result["unique_key"] = self.unique_key else: diff --git a/hc/api/urls.py b/hc/api/urls.py index 422ee154..d978e35f 100644 --- a/hc/api/urls.py +++ b/hc/api/urls.py @@ -38,6 +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/channels/", views.channels), path( "badge///.", diff --git a/hc/api/views.py b/hc/api/views.py index ada86f5a..b83dab56 100644 --- a/hc/api/views.py +++ b/hc/api/views.py @@ -1,4 +1,5 @@ from datetime import timedelta as td +from datetime import datetime import time import uuid @@ -8,6 +9,7 @@ from django.http import ( HttpResponse, HttpResponseForbidden, HttpResponseNotFound, + HttpResponseBadRequest, JsonResponse, ) from django.shortcuts import get_object_or_404 @@ -293,6 +295,57 @@ 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) + if check.project_id != request.project.id: + return HttpResponseForbidden() + + if any(x in request.GET for x in ('start','end')) and 'seconds' in request.GET: + return HttpResponseBadRequest() + + history_start = None + history_end = datetime.now() + + if 'start' in request.GET: + history_start = datetime.fromtimestamp(int(request.GET['start'])) + if 'end' in request.GET: + history_end = datetime.fromtimestamp(int(request.GET['end'])) + + if 'seconds' in request.GET: + history_start = datetime.now()-td(seconds=int(request.GET['seconds'])) + elif not history_start: + history_start = datetime.now()-td(seconds=3600) + + 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") + dictStatus = {"up":1,"down":0} + + return JsonResponse({"flips": list(map(lambda x: {'timestamp':x.created,'up':dictStatus[x.new_status]}, flips))}) + + # return JsonResponse(check.to_dict( + # readonly=request.readonly, + # history=( + # history_start,history_end + # ) + # )) + +@cors("GET") +@csrf_exempt +@validate_json() +@authorize_read +def get_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 HttpResponseNotFound() @never_cache @cors("GET") diff --git a/templates/docs/api.md b/templates/docs/api.md index dd1ca5b5..8b2ab1e4 100644 --- a/templates/docs/api.md +++ b/templates/docs/api.md @@ -746,6 +746,83 @@ curl SITE_ROOT/api/v1/checks/f618072a-7bde-4eee-af63-71a77c5723bc/pings/ \ ``` +## Get a list of check's flips {: #list-flips .rule } + +`GET SITE_ROOT/api/v1/checks//flips/` or `GET SITE_ROOT/api/v1/checks//flips/` + +Returns a list of flips this check has experienced. A flip is a change of status (up, or down). + +This endpoint returns the status of a check for the period of time passed according to the below parameters. If no parameters are passed, the default is to return flips occuring in the previous 3600 seconds (`/?seconds=3600`), which is the last hour. + +### Query String Parameters + +Either the seconds or the start (and end) parameters can be passed. Passing both the seconds parameter and the start/end parameters will return a 400 error (see below). + +seconds=<value> +: Filters the checks, and returns the flip history in the last `n` seconds + + Example: + + `SITE_ROOT/api/v1/checks//flips/?seconds=3600` + +start=<value>&end=<value> +: Filters the checks, and returns the flip history between the start and end timestamps. + + If provided, both values must be unix timestamps. The `end` parameter is optional and defaults to the timestamp of the current date and time. + + Example: + + `SITE_ROOT/api/v1/checks//flips/?seconds=3600` + + +### Response Codes + +200 OK +: The request succeeded. + +400 Bad Request +: Both a seconds and a start or end query string parameter has been passed, which is unsupported. + +401 Unauthorized +: The API key is either missing or invalid. + +403 Forbidden +: Access denied, wrong API key. + +404 Not Found +: The specified check does not exist. + +### Example Request + +```bash +curl SITE_ROOT/api/v1/checks/f618072a-7bde-4eee-af63-71a77c5723bc/flips/ \ + --header "X-Api-Key: your-api-key" +``` + +### Example Response + +```json +{ + "name": "My First Check", + "tags": "", + "desc": "", + "grace": 3600, + "n_pings": 2, + "status": "up", + "last_ping": "2020-06-12T02:18:46+00:00", + "next_ping": "2020-06-13T02:18:46+00:00", + "manual_resume": false, + "history": [ + { + "timestamp": "2020-03-23T23:30:18.767Z", + "up": 1 + } + ], + "unique_key": "e855898bebff1756cde7c571319d877d07a38dab", + "timeout": 86400 +} +``` + ## Get a List of Existing Integrations {: #list-channels .rule } `GET SITE_ROOT/api/v1/channels/`