mirror of
https://github.com/healthchecks/healthchecks.git
synced 2025-04-17 18:22:33 +00:00
Add "Get a List of Existing Integrations" API call
This commit is contained in:
parent
21de50d84e
commit
a7061fe6a5
11 changed files with 147 additions and 10 deletions
|
@ -1,7 +1,7 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## Unreleased
|
## 1.3.0 - 2018-11-21
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
- Load settings from environment variables
|
- Load settings from environment variables
|
||||||
|
@ -13,8 +13,9 @@ All notable changes to this project will be documented in this file.
|
||||||
- Show a warning when running with DEBUG=True
|
- Show a warning when running with DEBUG=True
|
||||||
- Add "channels" attribute to the Check API resource
|
- Add "channels" attribute to the Check API resource
|
||||||
- Can specify channel codes when updating a check via API
|
- Can specify channel codes when updating a check via API
|
||||||
- Added a workaround for email agents automatically opening "Unsubscribe" links
|
- Add a workaround for email agents automatically opening "Unsubscribe" links
|
||||||
- Add Channel.name field, users can now name integrations
|
- Add Channel.name field, users can now name integrations
|
||||||
|
- Add "Get a List of Existing Integrations" API call
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
- During DST transition, handle ambiguous dates as pre-transition
|
- During DST transition, handle ambiguous dates as pre-transition
|
||||||
|
|
|
@ -254,6 +254,13 @@ class Channel(models.Model):
|
||||||
|
|
||||||
return self.get_kind_display()
|
return self.get_kind_display()
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return {
|
||||||
|
"id": str(self.code),
|
||||||
|
"name": self.name,
|
||||||
|
"kind": self.kind
|
||||||
|
}
|
||||||
|
|
||||||
def assign_all_checks(self):
|
def assign_all_checks(self):
|
||||||
checks = Check.objects.filter(user=self.user)
|
checks = Check.objects.filter(user=self.user)
|
||||||
self.checks.add(*checks)
|
self.checks.add(*checks)
|
||||||
|
|
54
hc/api/tests/test_list_channels.py
Normal file
54
hc/api/tests/test_list_channels.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
from hc.api.models import Channel
|
||||||
|
from hc.test import BaseTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class ListChannelsTestCase(BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(ListChannelsTestCase, self).setUp()
|
||||||
|
|
||||||
|
self.c1 = Channel(user=self.alice)
|
||||||
|
self.c1.kind = "email"
|
||||||
|
self.c1.name = "Email to Alice"
|
||||||
|
self.c1.save()
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
return self.client.get("/api/v1/channels/", HTTP_X_API_KEY="X" * 32)
|
||||||
|
|
||||||
|
def test_it_works(self):
|
||||||
|
r = self.get()
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
|
doc = r.json()
|
||||||
|
self.assertEqual(len(doc["channels"]), 1)
|
||||||
|
|
||||||
|
c = doc["channels"][0]
|
||||||
|
self.assertEqual(c["id"], str(self.c1.code))
|
||||||
|
self.assertEqual(c["kind"], "email")
|
||||||
|
self.assertEqual(c["name"], "Email to Alice")
|
||||||
|
|
||||||
|
def test_it_shows_only_users_channels(self):
|
||||||
|
Channel.objects.create(user=self.bob, kind="email", name="Bob")
|
||||||
|
|
||||||
|
r = self.get()
|
||||||
|
data = r.json()
|
||||||
|
self.assertEqual(len(data["channels"]), 1)
|
||||||
|
for c in data["channels"]:
|
||||||
|
self.assertNotEqual(c["name"], "Bob")
|
||||||
|
|
||||||
|
def test_it_accepts_api_key_from_request_body(self):
|
||||||
|
payload = json.dumps({"api_key": "X" * 32})
|
||||||
|
r = self.client.generic("GET", "/api/v1/channels/", payload,
|
||||||
|
content_type="application/json")
|
||||||
|
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
self.assertContains(r, "Email to Alice")
|
||||||
|
|
||||||
|
def test_readonly_key_works(self):
|
||||||
|
self.profile.api_key_readonly = "R" * 32
|
||||||
|
self.profile.save()
|
||||||
|
|
||||||
|
r = self.client.get("/api/v1/channels/", HTTP_X_API_KEY="R" * 32)
|
||||||
|
self.assertEqual(r.status_code, 200)
|
|
@ -14,6 +14,9 @@ urlpatterns = [
|
||||||
path('api/v1/notifications/<uuid:code>/bounce', views.bounce,
|
path('api/v1/notifications/<uuid:code>/bounce', views.bounce,
|
||||||
name="hc-api-bounce"),
|
name="hc-api-bounce"),
|
||||||
|
|
||||||
|
path('api/v1/channels/', views.channels),
|
||||||
|
|
||||||
|
|
||||||
path('badge/<slug:username>/<slug:signature>/<slug:tag>.svg', views.badge,
|
path('badge/<slug:username>/<slug:signature>/<slug:tag>.svg', views.badge,
|
||||||
name="hc-badge"),
|
name="hc-badge"),
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ from django.shortcuts import get_object_or_404
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.views.decorators.cache import never_cache
|
from django.views.decorators.cache import never_cache
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
from django.views.decorators.http import require_POST
|
from django.views.decorators.http import require_GET, require_POST
|
||||||
|
|
||||||
from hc.api import schemas
|
from hc.api import schemas
|
||||||
from hc.api.decorators import authorize, authorize_read, validate_json
|
from hc.api.decorators import authorize, authorize_read, validate_json
|
||||||
|
@ -150,6 +150,15 @@ def checks(request):
|
||||||
return HttpResponse(status=405)
|
return HttpResponse(status=405)
|
||||||
|
|
||||||
|
|
||||||
|
@require_GET
|
||||||
|
@validate_json()
|
||||||
|
@authorize_read
|
||||||
|
def channels(request):
|
||||||
|
q = Channel.objects.filter(user=request.user)
|
||||||
|
channels = [ch.to_dict() for ch in q]
|
||||||
|
return JsonResponse({"channels": channels})
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@validate_json(schemas.check)
|
@validate_json(schemas.check)
|
||||||
@authorize
|
@authorize
|
||||||
|
|
|
@ -44,6 +44,8 @@ class Command(BaseCommand):
|
||||||
# API examples
|
# API examples
|
||||||
_process("list_checks_request", lexers.BashLexer())
|
_process("list_checks_request", lexers.BashLexer())
|
||||||
_process("list_checks_response", lexers.JsonLexer())
|
_process("list_checks_response", lexers.JsonLexer())
|
||||||
|
_process("list_channels_request", lexers.BashLexer())
|
||||||
|
_process("list_channels_response", lexers.JsonLexer())
|
||||||
_process("create_check_request_a", lexers.BashLexer())
|
_process("create_check_request_a", lexers.BashLexer())
|
||||||
_process("create_check_request_b", lexers.BashLexer())
|
_process("create_check_request_b", lexers.BashLexer())
|
||||||
_process("update_check_request_a", lexers.BashLexer())
|
_process("update_check_request_a", lexers.BashLexer())
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<h2>API Endpoints</h2>
|
<h2>API Endpoints</h2>
|
||||||
<table class="table table-bordered">
|
<table class="table table-bordered">
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="#list-checks">Get list of existing checks</a></td>
|
<td><a href="#list-checks">Get a list of existing checks</a></td>
|
||||||
<td>
|
<td>
|
||||||
<code>GET {{ SITE_ROOT }}/api/v1/checks/</code>
|
<code>GET {{ SITE_ROOT }}/api/v1/checks/</code>
|
||||||
</td>
|
</td>
|
||||||
|
@ -46,6 +46,12 @@
|
||||||
<code>DELETE {{ SITE_ROOT }}/api/v1/checks/<code></code>
|
<code>DELETE {{ SITE_ROOT }}/api/v1/checks/<code></code>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><a href="#list-channels">Get a list of existing integrations</a></td>
|
||||||
|
<td>
|
||||||
|
<code>GET {{ SITE_ROOT }}/api/v1/channels/</code>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>Authentication</h2>
|
<h2>Authentication</h2>
|
||||||
|
@ -90,7 +96,7 @@ The response may contain a JSON document with additional data.
|
||||||
<!-- ********************************************************************** /-->
|
<!-- ********************************************************************** /-->
|
||||||
|
|
||||||
<a class="section" name="list-checks">
|
<a class="section" name="list-checks">
|
||||||
<h2 class="rule">Get List of Existing Checks</h2>
|
<h2 class="rule">Get a List of Existing Checks</h2>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="api-path">GET {{ SITE_ROOT }}/api/v1/checks/</div>
|
<div class="api-path">GET {{ SITE_ROOT }}/api/v1/checks/</div>
|
||||||
|
@ -205,12 +211,18 @@ To create a "cron" check, specify the "schedule" and "tz" parameters.
|
||||||
<td>
|
<td>
|
||||||
<p>string, optional</p>
|
<p>string, optional</p>
|
||||||
<p>By default, if a check is created through API, no notification
|
<p>By default, if a check is created through API, no notification
|
||||||
channels are assigned to it. So, when the check goes up or down,
|
channels (integrations) are assigned to it.
|
||||||
no notifications will get sent.</p>
|
So, when the check goes up or down, no notifications will get
|
||||||
|
sent.</p>
|
||||||
|
|
||||||
<p>Set this field to a special value "*"
|
<p>Set this field to a special value "*"
|
||||||
to automatically assign all existing notification channels.</p>
|
to automatically assign all existing integrations.</p>
|
||||||
<p>To assign specific notification channels, use a comma-separated
|
|
||||||
list of channel identifiers.</p>
|
<p>To assign specific integrations, use a comma-separated
|
||||||
|
list of integration identifiers. Use the
|
||||||
|
<a href="#list-channels">Get a List of Existing Integrations</a>
|
||||||
|
call to look up integration identifiers.
|
||||||
|
</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -426,6 +438,23 @@ is sometimes required by some network proxies and web servers.
|
||||||
<h3 class="api-section">Example Response</h3>
|
<h3 class="api-section">Example Response</h3>
|
||||||
{% include "front/snippets/create_check_response.html" %}
|
{% include "front/snippets/create_check_response.html" %}
|
||||||
|
|
||||||
|
<!-- ********************************************************************** /-->
|
||||||
|
|
||||||
|
<a class="section" name="list-channels">
|
||||||
|
<h2 class="rule">Get a List of Existing Integrations</h2>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="api-path">GET {{ SITE_ROOT }}/api/v1/channels/</div>
|
||||||
|
|
||||||
|
<p>Returns a list of integrations belonging to the user.</p>
|
||||||
|
|
||||||
|
<h3 class="api-section">Example Request</h3>
|
||||||
|
{% include "front/snippets/list_channels_request.html" %}
|
||||||
|
|
||||||
|
<h3 class="api-section">Example Response</h3>
|
||||||
|
{% include "front/snippets/list_channels_response.html" %}
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
|
|
2
templates/front/snippets/list_channels_request.html
Normal file
2
templates/front/snippets/list_channels_request.html
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<div class="highlight"><pre><span></span>curl --header <span class="s2">"X-Api-Key: your-api-key"</span> {{ SITE_ROOT }}/api/v1/channels/
|
||||||
|
</pre></div>
|
1
templates/front/snippets/list_channels_request.txt
Normal file
1
templates/front/snippets/list_channels_request.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
curl --header "X-Api-Key: your-api-key" SITE_ROOT/api/v1/channels/
|
15
templates/front/snippets/list_channels_response.html
Normal file
15
templates/front/snippets/list_channels_response.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<div class="highlight"><pre><span></span><span class="p">{</span>
|
||||||
|
<span class="nt">"channels"</span><span class="p">:</span> <span class="p">[</span>
|
||||||
|
<span class="p">{</span>
|
||||||
|
<span class="nt">"id"</span><span class="p">:</span> <span class="s2">"4ec5a071-2d08-4baa-898a-eb4eb3cd6941"</span><span class="p">,</span>
|
||||||
|
<span class="nt">"name"</span><span class="p">:</span> <span class="s2">"My Work Email"</span><span class="p">,</span>
|
||||||
|
<span class="nt">"kind"</span><span class="p">:</span> <span class="s2">"email"</span>
|
||||||
|
<span class="p">},</span>
|
||||||
|
<span class="p">{</span>
|
||||||
|
<span class="nt">"id"</span><span class="p">:</span> <span class="s2">"746a083e-f542-4554-be1a-707ce16d3acc"</span><span class="p">,</span>
|
||||||
|
<span class="nt">"name"</span><span class="p">:</span> <span class="s2">"My Phone"</span><span class="p">,</span>
|
||||||
|
<span class="nt">"kind"</span><span class="p">:</span> <span class="s2">"sms"</span>
|
||||||
|
<span class="p">}</span>
|
||||||
|
<span class="p">]</span>
|
||||||
|
<span class="p">}</span>
|
||||||
|
</pre></div>
|
14
templates/front/snippets/list_channels_response.txt
Normal file
14
templates/front/snippets/list_channels_response.txt
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"channels": [
|
||||||
|
{
|
||||||
|
"id": "4ec5a071-2d08-4baa-898a-eb4eb3cd6941",
|
||||||
|
"name": "My Work Email",
|
||||||
|
"kind": "email"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "746a083e-f542-4554-be1a-707ce16d3acc",
|
||||||
|
"name": "My Phone",
|
||||||
|
"kind": "sms"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue