Browse Source

Merge branch 'jameskirsop-api-single-check'

pull/358/head
Pēteris Caune 5 years ago
parent
commit
ffc7ccddf2
No known key found for this signature in database GPG Key ID: E28D7679E9A9EDE2
7 changed files with 152 additions and 11 deletions
  1. +1
    -1
      hc/api/models.py
  2. +55
    -0
      hc/api/tests/test_get_check.py
  3. +0
    -5
      hc/api/tests/test_update_check.py
  4. +1
    -1
      hc/api/urls.py
  5. +17
    -4
      hc/api/views.py
  6. +38
    -0
      templates/docs/api.html
  7. +40
    -0
      templates/docs/api.md

+ 1
- 1
hc/api/models.py View File

@ -224,7 +224,7 @@ class Check(models.Model):
if readonly: if readonly:
result["unique_key"] = self.unique_key result["unique_key"] = self.unique_key
else: else:
update_rel_url = reverse("hc-api-update", args=[self.code])
update_rel_url = reverse("hc-api-single", args=[self.code])
pause_rel_url = reverse("hc-api-pause", args=[self.code]) pause_rel_url = reverse("hc-api-pause", args=[self.code])
result["ping_url"] = self.url() result["ping_url"] = self.url()


+ 55
- 0
hc/api/tests/test_get_check.py View File

@ -0,0 +1,55 @@
from datetime import timedelta as td
import uuid
from django.utils.timezone import now
from hc.api.models import Channel, Check
from hc.test import BaseTestCase
class GetCheckTestCase(BaseTestCase):
def setUp(self):
super(GetCheckTestCase, self).setUp()
self.now = now().replace(microsecond=0)
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.c1 = Channel.objects.create(project=self.project)
self.a1.channel_set.add(self.c1)
def get(self, code):
url = "/api/v1/checks/%s" % code
return self.client.get(url, HTTP_X_API_KEY="X" * 32)
def test_it_works(self):
r = self.get(self.a1.code)
self.assertEqual(r.status_code, 200)
self.assertEqual(r["Access-Control-Allow-Origin"], "*")
doc = r.json()
self.assertEqual(len(doc), 13)
self.assertEqual(doc["timeout"], 3600)
self.assertEqual(doc["grace"], 900)
self.assertEqual(doc["ping_url"], self.a1.url())
self.assertEqual(doc["last_ping"], None)
self.assertEqual(doc["n_pings"], 0)
self.assertEqual(doc["status"], "new")
self.assertEqual(doc["channels"], str(self.c1.code))
self.assertEqual(doc["desc"], "This is description")
def test_it_handles_invalid_uuid(self):
r = self.get("not-an-uuid")
self.assertEqual(r.status_code, 404)
def test_it_handles_missing_check(self):
made_up_code = "07c2f548-9850-4b27-af5d-6c9dc157ec02"
r = self.get(made_up_code)
self.assertEqual(r.status_code, 404)

+ 0
- 5
hc/api/tests/test_update_check.py View File

@ -72,11 +72,6 @@ class UpdateCheckTestCase(BaseTestCase):
check = Check.objects.get() check = Check.objects.get()
self.assertEqual(check.channel_set.count(), 0) self.assertEqual(check.channel_set.count(), 0)
def test_it_requires_post(self):
url = "/api/v1/checks/%s" % self.check.code
r = self.client.get(url, HTTP_X_API_KEY="X" * 32)
self.assertEqual(r.status_code, 405)
def test_it_handles_invalid_uuid(self): def test_it_handles_invalid_uuid(self):
r = self.post("not-an-uuid", {"api_key": "X" * 32}) r = self.post("not-an-uuid", {"api_key": "X" * 32})
self.assertEqual(r.status_code, 404) self.assertEqual(r.status_code, 404)


+ 1
- 1
hc/api/urls.py View File

@ -22,7 +22,7 @@ urlpatterns = [
path("ping/<uuid:code>/fail", views.ping, {"action": "fail"}, name="hc-fail"), path("ping/<uuid:code>/fail", views.ping, {"action": "fail"}, name="hc-fail"),
path("ping/<uuid:code>/start", views.ping, {"action": "start"}, name="hc-start"), path("ping/<uuid:code>/start", views.ping, {"action": "start"}, name="hc-start"),
path("api/v1/checks/", views.checks), path("api/v1/checks/", views.checks),
path("api/v1/checks/<uuid:code>", views.update, name="hc-api-update"),
path("api/v1/checks/<uuid:code>", views.single, name="hc-api-single"),
path("api/v1/checks/<uuid:code>/pause", views.pause, name="hc-api-pause"), path("api/v1/checks/<uuid:code>/pause", views.pause, name="hc-api-pause"),
path("api/v1/notifications/<uuid:code>/bounce", views.bounce, name="hc-api-bounce"), path("api/v1/notifications/<uuid:code>/bounce", views.bounce, name="hc-api-bounce"),
path("api/v1/channels/", views.channels), path("api/v1/channels/", views.channels),


+ 17
- 4
hc/api/views.py View File

@ -19,6 +19,7 @@ from hc.api import schemas
from hc.api.decorators import authorize, authorize_read, cors, validate_json from hc.api.decorators import authorize, authorize_read, cors, validate_json
from hc.api.models import Check, Notification, Channel from hc.api.models import Check, Notification, Channel
from hc.lib.badges import check_signature, get_badge_svg from hc.lib.badges import check_signature, get_badge_svg
from hc.lib.jsonschema import ValidationError, validate
class BadChannelException(Exception): class BadChannelException(Exception):
@ -178,18 +179,19 @@ def channels(request):
@csrf_exempt @csrf_exempt
@cors("POST", "DELETE")
@validate_json(schemas.check)
@cors("POST", "DELETE", "GET")
@validate_json()
@authorize @authorize
def update(request, code):
def single(request, code):
check = get_object_or_404(Check, code=code) check = get_object_or_404(Check, code=code)
if check.project != request.project: if check.project != request.project:
return HttpResponseForbidden() return HttpResponseForbidden()
if request.method == "POST": if request.method == "POST":
try: try:
validate(request.json, schemas.check)
_update(check, request.json) _update(check, request.json)
except BadChannelException as e:
except (BadChannelException,ValidationError) as e:
return JsonResponse({"error": str(e)}, status=400) return JsonResponse({"error": str(e)}, status=400)
return JsonResponse(check.to_dict()) return JsonResponse(check.to_dict())
@ -199,10 +201,21 @@ def update(request, code):
check.delete() check.delete()
return JsonResponse(response) return JsonResponse(response)
elif request.method == "GET":
return JsonResponse(check.to_dict())
# Otherwise, method not allowed # Otherwise, method not allowed
return HttpResponse(status=405) return HttpResponse(status=405)
@csrf_exempt
@cors("POST", "DELETE")
@validate_json(schemas.check)
@authorize
def update(request, code):
single(request, code)
@cors("POST") @cors("POST")
@csrf_exempt @csrf_exempt
@validate_json() @validate_json()


+ 38
- 0
templates/docs/api.html View File

@ -15,6 +15,10 @@ checks in user's account.</p>
<td><code>GET SITE_ROOT/api/v1/checks/</code></td> <td><code>GET SITE_ROOT/api/v1/checks/</code></td>
</tr> </tr>
<tr> <tr>
<td><a href="#get-check">Create a single check</a></td>
<td><code>GET SITE_ROOT/api/v1/checks/&lt;uuid&gt;</code></td>
</tr>
<tr>
<td><a href="#create-check">Create a new check</a></td> <td><a href="#create-check">Create a new check</a></td>
<td><code>POST SITE_ROOT/api/v1/checks/</code></td> <td><code>POST SITE_ROOT/api/v1/checks/</code></td>
</tr> </tr>
@ -174,6 +178,40 @@ is added. This identifier is stable across API calls. Example:</p>
</code></pre></div> </code></pre></div>
<h2 class="rule" id="get-check">Get a single Check</h2>
<p><code>GET SITE_ROOT/api/v1/checks/&lt;uuid&gt;</code></p>
<p>Returns a JSON object containing information information from a single check.</p>
<h3>Response Codes</h3>
<dl>
<dt>200 OK</dt>
<dd>The request succeeded.</dd>
<dt>401 Unauthorized</dt>
<dd>The API key is either missing or invalid.</dd>
</dl>
<h3>Example Request</h3>
<div class="highlight"><pre><span></span><code>curl --header <span class="s2">&quot;X-Api-Key: your-api-key&quot;</span> SITE_ROOT/api/v1/checks/&lt;uuid&gt;
</code></pre></div>
<h3>Example Response</h3>
<div class="highlight"><pre><span></span><code><span class="p">{</span>
<span class="nt">&quot;channels&quot;</span><span class="p">:</span> <span class="s2">&quot;4ec5a071-2d08-4baa-898a-eb4eb3cd6941,746a083e-f542-4554-be1a-707ce16d3acc&quot;</span><span class="p">,</span>
<span class="nt">&quot;desc&quot;</span><span class="p">:</span> <span class="s2">&quot;Longer free-form description goes here&quot;</span><span class="p">,</span>
<span class="nt">&quot;grace&quot;</span><span class="p">:</span> <span class="mi">900</span><span class="p">,</span>
<span class="nt">&quot;last_ping&quot;</span><span class="p">:</span> <span class="s2">&quot;2017-01-04T13:24:39.903464+00:00&quot;</span><span class="p">,</span>
<span class="nt">&quot;n_pings&quot;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Api test 1&quot;</span><span class="p">,</span>
<span class="nt">&quot;next_ping&quot;</span><span class="p">:</span> <span class="s2">&quot;2017-01-04T14:24:39.903464+00:00&quot;</span><span class="p">,</span>
<span class="nt">&quot;pause_url&quot;</span><span class="p">:</span> <span class="s2">&quot;SITE_ROOT/api/v1/checks/662ebe36-ecab-48db-afe3-e20029cb71e6/pause&quot;</span><span class="p">,</span>
<span class="nt">&quot;ping_url&quot;</span><span class="p">:</span> <span class="s2">&quot;PING_ENDPOINT662ebe36-ecab-48db-afe3-e20029cb71e6&quot;</span><span class="p">,</span>
<span class="nt">&quot;status&quot;</span><span class="p">:</span> <span class="s2">&quot;up&quot;</span><span class="p">,</span>
<span class="nt">&quot;tags&quot;</span><span class="p">:</span> <span class="s2">&quot;foo&quot;</span><span class="p">,</span>
<span class="nt">&quot;timeout&quot;</span><span class="p">:</span> <span class="mi">3600</span><span class="p">,</span>
<span class="nt">&quot;update_url&quot;</span><span class="p">:</span> <span class="s2">&quot;SITE_ROOT/api/v1/checks/662ebe36-ecab-48db-afe3-e20029cb71e6&quot;</span>
<span class="p">}</span>
</code></pre></div>
<h2 class="rule" id="create-check">Create a Check</h2> <h2 class="rule" id="create-check">Create a Check</h2>
<p><code>POST SITE_ROOT/api/v1/checks/</code></p> <p><code>POST SITE_ROOT/api/v1/checks/</code></p>
<p>Creates a new check and returns its ping URL. <p>Creates a new check and returns its ping URL.


+ 40
- 0
templates/docs/api.md View File

@ -8,6 +8,7 @@ checks in user's account.
Endpoint Name | Endpoint Address Endpoint Name | Endpoint Address
------------------------------------------------------|------- ------------------------------------------------------|-------
[Get a list of existing checks](#list-checks) | `GET SITE_ROOT/api/v1/checks/` [Get a list of existing checks](#list-checks) | `GET SITE_ROOT/api/v1/checks/`
[Create a single check](#get-check) | `GET SITE_ROOT/api/v1/checks/<uuid>`
[Create a new check](#create-check) | `POST SITE_ROOT/api/v1/checks/` [Create a new check](#create-check) | `POST SITE_ROOT/api/v1/checks/`
[Update an existing check](#update-check) | `POST SITE_ROOT/api/v1/checks/<uuid>` [Update an existing check](#update-check) | `POST SITE_ROOT/api/v1/checks/<uuid>`
[Pause monitoring of a check](#pause-check) | `POST SITE_ROOT/api/v1/checks/<uuid>/pause` [Pause monitoring of a check](#pause-check) | `POST SITE_ROOT/api/v1/checks/<uuid>/pause`
@ -157,6 +158,45 @@ is added. This identifier is stable across API calls. Example:
} }
``` ```
## Get a single Check {: #get-check .rule }
`GET SITE_ROOT/api/v1/checks/<uuid>`
Returns a JSON object containing information information from a single check.
### Response Codes
200 OK
: The request succeeded.
401 Unauthorized
: The API key is either missing or invalid.
### Example Request
```bash
curl --header "X-Api-Key: your-api-key" SITE_ROOT/api/v1/checks/<uuid>
```
### Example Response
```json
{
"channels": "4ec5a071-2d08-4baa-898a-eb4eb3cd6941,746a083e-f542-4554-be1a-707ce16d3acc",
"desc": "Longer free-form description goes here",
"grace": 900,
"last_ping": "2017-01-04T13:24:39.903464+00:00",
"n_pings": 1,
"name": "Api test 1",
"next_ping": "2017-01-04T14:24:39.903464+00:00",
"pause_url": "SITE_ROOT/api/v1/checks/662ebe36-ecab-48db-afe3-e20029cb71e6/pause",
"ping_url": "PING_ENDPOINT662ebe36-ecab-48db-afe3-e20029cb71e6",
"status": "up",
"tags": "foo",
"timeout": 3600,
"update_url": "SITE_ROOT/api/v1/checks/662ebe36-ecab-48db-afe3-e20029cb71e6"
}
```
## Create a Check {: #create-check .rule } ## Create a Check {: #create-check .rule }
`POST SITE_ROOT/api/v1/checks/` `POST SITE_ROOT/api/v1/checks/`


Loading…
Cancel
Save