0
0
Fork 0
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:
Pēteris Caune 2018-11-21 20:21:04 +02:00
parent 21de50d84e
commit a7061fe6a5
No known key found for this signature in database
GPG key ID: E28D7679E9A9EDE2
11 changed files with 147 additions and 10 deletions

View file

@ -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

View file

@ -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)

View 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)

View file

@ -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"),

View file

@ -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

View file

@ -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())

View file

@ -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/&lt;code&gt;</code> <code>DELETE {{ SITE_ROOT }}/api/v1/checks/&lt;code&gt;</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 %}

View file

@ -0,0 +1,2 @@
<div class="highlight"><pre><span></span>curl --header <span class="s2">&quot;X-Api-Key: your-api-key&quot;</span> {{ SITE_ROOT }}/api/v1/channels/
</pre></div>

View file

@ -0,0 +1 @@
curl --header "X-Api-Key: your-api-key" SITE_ROOT/api/v1/channels/

View file

@ -0,0 +1,15 @@
<div class="highlight"><pre><span></span><span class="p">{</span>
<span class="nt">&quot;channels&quot;</span><span class="p">:</span> <span class="p">[</span>
<span class="p">{</span>
<span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="s2">&quot;4ec5a071-2d08-4baa-898a-eb4eb3cd6941&quot;</span><span class="p">,</span>
<span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;My Work Email&quot;</span><span class="p">,</span>
<span class="nt">&quot;kind&quot;</span><span class="p">:</span> <span class="s2">&quot;email&quot;</span>
<span class="p">},</span>
<span class="p">{</span>
<span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="s2">&quot;746a083e-f542-4554-be1a-707ce16d3acc&quot;</span><span class="p">,</span>
<span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;My Phone&quot;</span><span class="p">,</span>
<span class="nt">&quot;kind&quot;</span><span class="p">:</span> <span class="s2">&quot;sms&quot;</span>
<span class="p">}</span>
<span class="p">]</span>
<span class="p">}</span>
</pre></div>

View 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"
}
]
}