diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d531ea7..d4a3602a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Changelog All notable changes to this project will be documented in this file. -## Unreleased +## 1.3.0 - 2018-11-21 ### Improvements - 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 - Add "channels" attribute to the Check API resource - 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 "Get a List of Existing Integrations" API call ### Bug Fixes - During DST transition, handle ambiguous dates as pre-transition diff --git a/hc/api/models.py b/hc/api/models.py index 5a1a3471..358031e2 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -254,6 +254,13 @@ class Channel(models.Model): 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): checks = Check.objects.filter(user=self.user) self.checks.add(*checks) diff --git a/hc/api/tests/test_list_channels.py b/hc/api/tests/test_list_channels.py new file mode 100644 index 00000000..4f884b02 --- /dev/null +++ b/hc/api/tests/test_list_channels.py @@ -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) diff --git a/hc/api/urls.py b/hc/api/urls.py index 6fbec827..068a28e3 100644 --- a/hc/api/urls.py +++ b/hc/api/urls.py @@ -14,6 +14,9 @@ urlpatterns = [ path('api/v1/notifications//bounce', views.bounce, name="hc-api-bounce"), + path('api/v1/channels/', views.channels), + + path('badge///.svg', views.badge, name="hc-badge"), diff --git a/hc/api/views.py b/hc/api/views.py index 61e9429f..efa2a452 100644 --- a/hc/api/views.py +++ b/hc/api/views.py @@ -10,7 +10,7 @@ from django.shortcuts import get_object_or_404 from django.utils import timezone from django.views.decorators.cache import never_cache 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.decorators import authorize, authorize_read, validate_json @@ -150,6 +150,15 @@ def checks(request): 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 @validate_json(schemas.check) @authorize diff --git a/hc/front/management/commands/pygmentize.py b/hc/front/management/commands/pygmentize.py index b05077c7..b068fc24 100644 --- a/hc/front/management/commands/pygmentize.py +++ b/hc/front/management/commands/pygmentize.py @@ -44,6 +44,8 @@ class Command(BaseCommand): # API examples _process("list_checks_request", lexers.BashLexer()) _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_b", lexers.BashLexer()) _process("update_check_request_a", lexers.BashLexer()) diff --git a/templates/front/docs_api.html b/templates/front/docs_api.html index 35dd6636..42dc2965 100644 --- a/templates/front/docs_api.html +++ b/templates/front/docs_api.html @@ -17,7 +17,7 @@

API Endpoints

- + @@ -46,6 +46,12 @@ DELETE {{ SITE_ROOT }}/api/v1/checks/<code> + + + +
Get list of existing checksGet a list of existing checks GET {{ SITE_ROOT }}/api/v1/checks/
Get a list of existing integrations + GET {{ SITE_ROOT }}/api/v1/channels/ +

Authentication

@@ -90,7 +96,7 @@ The response may contain a JSON document with additional data. -

Get List of Existing Checks

+

Get a List of Existing Checks

GET {{ SITE_ROOT }}/api/v1/checks/
@@ -205,12 +211,18 @@ To create a "cron" check, specify the "schedule" and "tz" parameters.

string, optional

By default, if a check is created through API, no notification - channels are assigned to it. So, when the check goes up or down, - no notifications will get sent.

+ channels (integrations) are assigned to it. + So, when the check goes up or down, no notifications will get + sent.

+

Set this field to a special value "*" - to automatically assign all existing notification channels.

-

To assign specific notification channels, use a comma-separated - list of channel identifiers.

+ to automatically assign all existing integrations.

+ +

To assign specific integrations, use a comma-separated + list of integration identifiers. Use the + Get a List of Existing Integrations + call to look up integration identifiers. +

@@ -426,6 +438,23 @@ is sometimes required by some network proxies and web servers.

Example Response

{% include "front/snippets/create_check_response.html" %} + + + +

Get a List of Existing Integrations

+
+ +
GET {{ SITE_ROOT }}/api/v1/channels/
+ +

Returns a list of integrations belonging to the user.

+ +

Example Request

+{% include "front/snippets/list_channels_request.html" %} + +

Example Response

+{% include "front/snippets/list_channels_response.html" %} + + {% endblock %} {% block scripts %} diff --git a/templates/front/snippets/list_channels_request.html b/templates/front/snippets/list_channels_request.html new file mode 100644 index 00000000..bdb42f64 --- /dev/null +++ b/templates/front/snippets/list_channels_request.html @@ -0,0 +1,2 @@ +
curl --header "X-Api-Key: your-api-key" {{ SITE_ROOT }}/api/v1/channels/
+
diff --git a/templates/front/snippets/list_channels_request.txt b/templates/front/snippets/list_channels_request.txt new file mode 100644 index 00000000..abefe167 --- /dev/null +++ b/templates/front/snippets/list_channels_request.txt @@ -0,0 +1 @@ +curl --header "X-Api-Key: your-api-key" SITE_ROOT/api/v1/channels/ \ No newline at end of file diff --git a/templates/front/snippets/list_channels_response.html b/templates/front/snippets/list_channels_response.html new file mode 100644 index 00000000..de1d81a0 --- /dev/null +++ b/templates/front/snippets/list_channels_response.html @@ -0,0 +1,15 @@ +
{
+  "channels": [
+    {
+      "id": "4ec5a071-2d08-4baa-898a-eb4eb3cd6941",
+      "name": "My Work Email",
+      "kind": "email"
+    },
+    {
+      "id": "746a083e-f542-4554-be1a-707ce16d3acc",
+      "name": "My Phone",
+      "kind": "sms"
+    }
+  ]
+}
+
diff --git a/templates/front/snippets/list_channels_response.txt b/templates/front/snippets/list_channels_response.txt new file mode 100644 index 00000000..ded65355 --- /dev/null +++ b/templates/front/snippets/list_channels_response.txt @@ -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" + } + ] +} \ No newline at end of file