From b64c8d1cb8722ec4d0082aa760d55010dfaae0e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C4=93teris=20Caune?= Date: Thu, 10 Sep 2020 10:29:44 +0300 Subject: [PATCH] API support for setting the allowed HTTP methods for making ping requests --- CHANGELOG.md | 1 + hc/api/models.py | 1 + hc/api/schemas.py | 1 + hc/api/tests/test_create_check.py | 13 ++++++ hc/api/tests/test_get_check.py | 5 +- hc/api/tests/test_update_check.py | 33 +++++++++++++ hc/api/views.py | 3 ++ templates/docs/api.html | 69 +++++++++++++++++++-------- templates/docs/api.md | 77 +++++++++++++++++++++++-------- templates/docs/resources.html | 2 +- 10 files changed, 164 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0ed9c02..8025f9ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to this project will be documented in this file. - Host a read-only dashboard (from github.com/healthchecks/dashboard/) - LINE Notify integration (#412) - Read-only team members +- API support for setting the allowed HTTP methods for making ping requests ## Bug Fixes - Handle excessively long email addresses in the signup form diff --git a/hc/api/models.py b/hc/api/models.py index 55adfad5..8d22efec 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -225,6 +225,7 @@ class Check(models.Model): "last_ping": isostring(self.last_ping), "next_ping": isostring(self.get_grace_start()), "manual_resume": self.manual_resume, + "methods": self.methods, } if self.last_duration: diff --git a/hc/api/schemas.py b/hc/api/schemas.py index b7117b9d..c7c817ad 100644 --- a/hc/api/schemas.py +++ b/hc/api/schemas.py @@ -10,6 +10,7 @@ check = { "tz": {"type": "string", "format": "timezone", "maxLength": 36}, "channels": {"type": "string"}, "manual_resume": {"type": "boolean"}, + "methods": {"enum": ["", "POST"]}, "unique": { "type": "array", "items": {"enum": ["name", "tags", "timeout", "grace"]}, diff --git a/hc/api/tests/test_create_check.py b/hc/api/tests/test_create_check.py index 5f836bea..d57e7827 100644 --- a/hc/api/tests/test_create_check.py +++ b/hc/api/tests/test_create_check.py @@ -40,6 +40,7 @@ class CreateCheckTestCase(BaseTestCase): self.assertEqual(doc["tags"], "bar,baz") self.assertEqual(doc["last_ping"], None) self.assertEqual(doc["n_pings"], 0) + self.assertEqual(doc["methods"], "") self.assertTrue("schedule" not in doc) self.assertTrue("tz" not in doc) @@ -47,6 +48,7 @@ class CreateCheckTestCase(BaseTestCase): check = Check.objects.get() self.assertEqual(check.name, "Foo") self.assertEqual(check.tags, "bar,baz") + self.assertEqual(check.methods, "") self.assertEqual(check.timeout.total_seconds(), 3600) self.assertEqual(check.grace.total_seconds(), 60) self.assertEqual(check.project, self.project) @@ -243,3 +245,14 @@ class CreateCheckTestCase(BaseTestCase): r = self.post({"api_key": "X" * 32, "manual_resume": "surprise"}) self.assertEqual(r.status_code, 400) + + def test_it_sets_methods(self): + r = self.post({"api_key": "X" * 32, "methods": "POST"}) + + self.assertEqual(r.status_code, 201) + check = Check.objects.get() + self.assertEqual(check.methods, "POST") + + def test_it_rejects_bad_methods_value(self): + r = self.post({"api_key": "X" * 32, "methods": "bad-value"}) + self.assertEqual(r.status_code, 400) diff --git a/hc/api/tests/test_get_check.py b/hc/api/tests/test_get_check.py index 268f99eb..b0d24061 100644 --- a/hc/api/tests/test_get_check.py +++ b/hc/api/tests/test_get_check.py @@ -33,7 +33,7 @@ class GetCheckTestCase(BaseTestCase): self.assertEqual(r["Access-Control-Allow-Origin"], "*") doc = r.json() - self.assertEqual(len(doc), 14) + self.assertEqual(len(doc), 15) self.assertEqual(doc["timeout"], 3600) self.assertEqual(doc["grace"], 900) @@ -44,6 +44,7 @@ class GetCheckTestCase(BaseTestCase): self.assertEqual(doc["channels"], str(self.c1.code)) self.assertEqual(doc["desc"], "This is description") self.assertFalse(doc["manual_resume"]) + self.assertEqual(doc["methods"], "") def test_it_handles_invalid_uuid(self): r = self.get("not-an-uuid") @@ -60,7 +61,7 @@ class GetCheckTestCase(BaseTestCase): self.assertEqual(r["Access-Control-Allow-Origin"], "*") doc = r.json() - self.assertEqual(len(doc), 14) + self.assertEqual(len(doc), 15) self.assertEqual(doc["timeout"], 3600) self.assertEqual(doc["grace"], 900) diff --git a/hc/api/tests/test_update_check.py b/hc/api/tests/test_update_check.py index 5e3a2c17..cd6f5a16 100644 --- a/hc/api/tests/test_update_check.py +++ b/hc/api/tests/test_update_check.py @@ -242,3 +242,36 @@ class UpdateCheckTestCase(BaseTestCase): self.check.refresh_from_db() self.assertFalse(self.check.manual_resume) + + def test_it_sets_methods(self): + r = self.post(self.check.code, {"api_key": "X" * 32, "methods": "POST"}) + self.assertEqual(r.status_code, 200) + + self.check.refresh_from_db() + self.assertEqual(self.check.methods, "POST") + + def test_it_clears_methods(self): + self.check.methods = "POST" + self.check.save() + + # Client supplies an empty string: we should save it + r = self.post(self.check.code, {"api_key": "X" * 32, "methods": ""}) + self.assertEqual(r.status_code, 200) + + self.check.refresh_from_db() + self.assertEqual(self.check.methods, "") + + def test_it_leaves_methods_unchanged(self): + self.check.methods = "POST" + self.check.save() + + # Client omits the methods key: we should leave it unchanged + r = self.post(self.check.code, {"api_key": "X" * 32}) + self.assertEqual(r.status_code, 200) + + self.check.refresh_from_db() + self.assertEqual(self.check.methods, "POST") + + def test_it_rejects_bad_methods_value(self): + r = self.post(self.check.code, {"api_key": "X" * 32, "methods": "bad-value"}) + self.assertEqual(r.status_code, 400) diff --git a/hc/api/views.py b/hc/api/views.py index 87924edd..ee52152d 100644 --- a/hc/api/views.py +++ b/hc/api/views.py @@ -98,6 +98,9 @@ def _update(check, spec): if "manual_resume" in spec: check.manual_resume = spec["manual_resume"] + if "methods" in spec: + check.methods = spec["methods"] + if "timeout" in spec and "schedule" not in spec: check.kind = "simple" check.timeout = td(seconds=spec["timeout"]) diff --git a/templates/docs/api.html b/templates/docs/api.html index 4ad10219..30e9192f 100644 --- a/templates/docs/api.html +++ b/templates/docs/api.html @@ -120,6 +120,7 @@ specified value.

"last_ping": "2020-03-24T14:02:03+00:00", "next_ping": "2020-03-24T15:02:03+00:00", "manual_resume": false, + "methods": "", "ping_url": "PING_ENDPOINT31365bce-8da9-4729-8ff3-aaa71d56b712", "update_url": "SITE_ROOT/api/v1/checks/31365bce-8da9-4729-8ff3-aaa71d56b712", "pause_url": "SITE_ROOT/api/v1/checks/31365bce-8da9-4729-8ff3-aaa71d56b712/pause", @@ -136,6 +137,7 @@ specified value.

"last_ping": "2020-03-23T10:19:32+00:00", "next_ping": null, "manual_resume": false, + "methods": "", "ping_url": "PING_ENDPOINT803f680d-e89b-492b-82ef-2be7b774a92d", "update_url": "SITE_ROOT/api/v1/checks/803f680d-e89b-492b-82ef-2be7b774a92d", "pause_url": "SITE_ROOT/api/v1/checks/803f680d-e89b-492b-82ef-2be7b774a92d/pause", @@ -166,6 +168,7 @@ and Get a list of check's status changes API calls.

"last_ping": "2020-03-24T14:02:03+00:00", "next_ping": "2020-03-24T15:02:03+00:00", "manual_resume": false, + "methods": "", "unique_key": "a6c7b0a8a66bed0df66abfdab3c77736861703ee", "timeout": 3600 }, @@ -179,6 +182,7 @@ and Get a list of check's status changes API calls.

"last_ping": "2020-03-23T10:19:32+00:00", "next_ping": null, "manual_resume": false, + "methods": "", "unique_key": "124f983e0e3dcaeba921cfcef46efd084576e783", "schedule": "15 5 * * *", "tz": "UTC" @@ -221,6 +225,7 @@ using the read-only API key) as an identifier.

"last_ping": "2020-03-23T10:19:32+00:00", "next_ping": null, "manual_resume": false, + "methods": "", "ping_url": "PING_ENDPOINT803f680d-e89b-492b-82ef-2be7b774a92d", "update_url": "SITE_ROOT/api/v1/checks/803f680d-e89b-492b-82ef-2be7b774a92d", "pause_url": "SITE_ROOT/api/v1/checks/803f680d-e89b-492b-82ef-2be7b774a92d/pause", @@ -248,6 +253,7 @@ check's unique UUID.

"last_ping": "2020-03-23T10:19:32+00:00", "next_ping": null, "manual_resume": false, + "methods": "", "unique_key": "124f983e0e3dcaeba921cfcef46efd084576e783", "schedule": "15 5 * * *", "tz": "UTC" @@ -282,7 +288,7 @@ Example:

desc

string, optional.

-

Description for the check.

+

Description of the check.

timeout
@@ -300,7 +306,7 @@ Example:

schedule
-

string, optional, default value: " * * *".

+

string, optional, default value: "* * * * *".

A cron expression defining this check's schedule.

If you specify both timeout and schedule parameters, SITE_NAME will create a Cron check and ignore @@ -311,7 +317,7 @@ the timeout value.

tz

string, optional, default value: "UTC".

-

Server's timezone. This setting only has effect in combination with the +

Server's timezone. This setting only has an effect in combination with the schedule parameter.

Example:

{"tz": "Europe/Riga"}

@@ -324,6 +330,17 @@ or not. If set to false, a paused check will leave the paused state when it rece a ping. If set to true, a paused check will ignore pings and stay paused until you manually resume it from the web dashboard.

+
methods
+
+

string, optional, default value: "".

+

Specifies the allowed HTTP methods for making ping requests. +Must be one of the two values: "" (an empty string) or "POST".

+

Set this field to "" (an empty string) to allow HEAD, GET, +and POST requests.

+

Set this field to "POST" to allow only POST requests.

+

Example:

+

{"methods": "POST"}

+
channels

string, optional

@@ -340,11 +357,11 @@ API call to look up the available integration identifiers.

array of string values, optional, default value: [].

Enables "upsert" functionality. Before creating a check, SITE_NAME looks for existing checks, filtered by fields listed in unique.

-

If no matching check is found, SITE_NAME creates a new check and returns it +

If SITE_NAME does not find a matching check, it creates a new check and returns it with the HTTP status code 201.

-

If a matching check is found, SITE_NAME will update it -and return it with HTTP status code 200.

-

The accepted values for the unique field are: +

If SITE_NAME finds a matching check, it updates the existing check and +and returns it with HTTP status code 200.

+

The accepted values for the unique field are name, tags, timeout and grace.

Example:

{"name": "Backups", unique: ["name"]}

@@ -364,7 +381,7 @@ field values.
401 Unauthorized
The API key is either missing or invalid.
403 Forbidden
-
The account's check limit has been reached. For free accounts, +
The account has hit its check limit. For free accounts, the limit is 20 checks per account.

Example Request

@@ -390,6 +407,7 @@ the limit is 20 checks per account. "name": "Backups", "next_ping": null, "manual_resume": false, + "methods": "", "pause_url": "SITE_ROOT/api/v1/checks/f618072a-7bde-4eee-af63-71a77c5723bc/pause", "ping_url": "PING_ENDPOINTf618072a-7bde-4eee-af63-71a77c5723bc", "status": "new", @@ -402,9 +420,8 @@ the limit is 20 checks per account.

Update an Existing Check

POST SITE_ROOT/api/v1/checks/<uuid>

-

Updates an existing check. All request parameters are optional. The check is -updated only with the supplied request parameters. If any parameter is omitted, -its value is left unchanged.

+

Updates an existing check. All request parameters are optional. If you omit any +parameter, SITE_NAME will leave its value unchanged.

Request Parameters

name
@@ -422,7 +439,7 @@ its value is left unchanged.

desc

string, optional.

-

Description for the check.

+

Description of the check.

timeout
@@ -442,15 +459,16 @@ its value is left unchanged.

string, optional.

A cron expression defining this check's schedule.

-

If you specify both "timeout" and "schedule" parameters, "timeout" will be -ignored and "schedule" will be used.

+

If you specify both timeout and schedule parameters, +SITE_NAME will save the schedule parameter and ignore +the timeout.

Example for a check running every half-hour:

{"schedule": "0,30 *  * "}

tz

string, optional.

-

Server's timezone. This setting only has effect in combination with the +

Server's timezone. This setting only has an effect in combination with the "schedule" parameter.

Example:

{"tz": "Europe/Riga"}

@@ -458,11 +476,21 @@ ignored and "schedule" will be used.

manual_resume

boolean, optional, default value: false.

-

Controls whether a paused ping resumes automatically when pinged (the default), +

Controls whether a paused ping automatically resumes when pinged (the default), or not. If set to false, a paused check will leave the paused state when it receives -a ping. If set to true, a paused check will ignore pings and stay paused until it is -either manually resumed from the web dashboard or the manual_resume flag is -changed.

+a ping. If set to true, a paused check will ignore pings and stay paused until +you manually resume it from the web dashboard.

+
+
methods
+
+

string, optional, default value: "".

+

Specifies the allowed HTTP methods for making ping requests. +Must be one of the two values: "" (an empty string) or "POST".

+

Set this field to "" (an empty string) to allow HEAD, GET, +and POST requests.

+

Set this field to "POST" to allow only POST requests.

+

Example:

+

{"methods": "POST"}

channels
@@ -514,6 +542,7 @@ field values.
"name": "Backups", "next_ping": null, "manual_resume": false, + "methods": "", "pause_url": "SITE_ROOT/api/v1/checks/f618072a-7bde-4eee-af63-71a77c5723bc/pause", "ping_url": "PING_ENDPOINTf618072a-7bde-4eee-af63-71a77c5723bc", "status": "new", @@ -559,6 +588,7 @@ header is sometimes required by some network proxies and web servers.

"name": "Backups", "next_ping": null, "manual_resume": false, + "methods": "", "pause_url": "SITE_ROOT/api/v1/checks/f618072a-7bde-4eee-af63-71a77c5723bc/pause", "ping_url": "PING_ENDPOINTf618072a-7bde-4eee-af63-71a77c5723bc", "status": "paused", @@ -601,6 +631,7 @@ check that was just deleted.

"name": "Backups", "next_ping": null, "manual_resume": false, + "methods": "", "pause_url": "SITE_ROOT/api/v1/checks/f618072a-7bde-4eee-af63-71a77c5723bc/pause", "ping_url": "PING_ENDPOINTf618072a-7bde-4eee-af63-71a77c5723bc", "status": "new", diff --git a/templates/docs/api.md b/templates/docs/api.md index a5fd1508..850e7f72 100644 --- a/templates/docs/api.md +++ b/templates/docs/api.md @@ -103,6 +103,7 @@ curl --header "X-Api-Key: your-api-key" SITE_ROOT/api/v1/checks/ "last_ping": "2020-03-24T14:02:03+00:00", "next_ping": "2020-03-24T15:02:03+00:00", "manual_resume": false, + "methods": "", "ping_url": "PING_ENDPOINT31365bce-8da9-4729-8ff3-aaa71d56b712", "update_url": "SITE_ROOT/api/v1/checks/31365bce-8da9-4729-8ff3-aaa71d56b712", "pause_url": "SITE_ROOT/api/v1/checks/31365bce-8da9-4729-8ff3-aaa71d56b712/pause", @@ -119,6 +120,7 @@ curl --header "X-Api-Key: your-api-key" SITE_ROOT/api/v1/checks/ "last_ping": "2020-03-23T10:19:32+00:00", "next_ping": null, "manual_resume": false, + "methods": "", "ping_url": "PING_ENDPOINT803f680d-e89b-492b-82ef-2be7b774a92d", "update_url": "SITE_ROOT/api/v1/checks/803f680d-e89b-492b-82ef-2be7b774a92d", "pause_url": "SITE_ROOT/api/v1/checks/803f680d-e89b-492b-82ef-2be7b774a92d/pause", @@ -151,6 +153,7 @@ Example: "last_ping": "2020-03-24T14:02:03+00:00", "next_ping": "2020-03-24T15:02:03+00:00", "manual_resume": false, + "methods": "", "unique_key": "a6c7b0a8a66bed0df66abfdab3c77736861703ee", "timeout": 3600 }, @@ -164,6 +167,7 @@ Example: "last_ping": "2020-03-23T10:19:32+00:00", "next_ping": null, "manual_resume": false, + "methods": "", "unique_key": "124f983e0e3dcaeba921cfcef46efd084576e783", "schedule": "15 5 * * *", "tz": "UTC" @@ -214,6 +218,7 @@ curl --header "X-Api-Key: your-api-key" SITE_ROOT/api/v1/checks/ "last_ping": "2020-03-23T10:19:32+00:00", "next_ping": null, "manual_resume": false, + "methods": "", "ping_url": "PING_ENDPOINT803f680d-e89b-492b-82ef-2be7b774a92d", "update_url": "SITE_ROOT/api/v1/checks/803f680d-e89b-492b-82ef-2be7b774a92d", "pause_url": "SITE_ROOT/api/v1/checks/803f680d-e89b-492b-82ef-2be7b774a92d/pause", @@ -244,6 +249,7 @@ check's unique UUID. "last_ping": "2020-03-23T10:19:32+00:00", "next_ping": null, "manual_resume": false, + "methods": "", "unique_key": "124f983e0e3dcaeba921cfcef46efd084576e783", "schedule": "15 5 * * *", "tz": "UTC" @@ -281,7 +287,7 @@ tags desc : string, optional. - Description for the check. + Description of the check. timeout : number, optional, default value: {{ default_timeout }}. @@ -302,7 +308,7 @@ grace Minimum: 60 (one minute), maximum: 2592000 (30 days). schedule -: string, optional, default value: "* * * * *". +: string, optional, default value: "`* * * * *`". A cron expression defining this check's schedule. @@ -317,7 +323,7 @@ schedule tz : string, optional, default value: "UTC". - Server's timezone. This setting only has effect in combination with the + Server's timezone. This setting only has an effect in combination with the `schedule` parameter. Example: @@ -332,6 +338,21 @@ manual_resume a ping. If set to true, a paused check will ignore pings and stay paused until you manually resume it from the web dashboard. +methods +: string, optional, default value: "". + + Specifies the allowed HTTP methods for making ping requests. + Must be one of the two values: "" (an empty string) or "POST". + + Set this field to "" (an empty string) to allow HEAD, GET, + and POST requests. + + Set this field to "POST" to allow only POST requests. + + Example: + +
{"methods": "POST"}
+ channels : string, optional @@ -351,13 +372,13 @@ unique Enables "upsert" functionality. Before creating a check, SITE_NAME looks for existing checks, filtered by fields listed in `unique`. - If no matching check is found, SITE_NAME creates a new check and returns it + If SITE_NAME does not find a matching check, it creates a new check and returns it with the HTTP status code 201. - If a matching check *is* found, SITE_NAME will update it - and return it with HTTP status code 200. + If SITE_NAME finds a matching check, it updates the existing check and + and returns it with HTTP status code 200. - The accepted values for the `unique` field are: + The accepted values for the `unique` field are `name`, `tags`, `timeout` and `grace`. Example: @@ -383,7 +404,7 @@ unique : The API key is either missing or invalid. 403 Forbidden -: The account's check limit has been reached. For free accounts, +: The account has hit its check limit. For free accounts, the limit is 20 checks per account. ### Example Request @@ -413,6 +434,7 @@ curl SITE_ROOT/api/v1/checks/ \ "name": "Backups", "next_ping": null, "manual_resume": false, + "methods": "", "pause_url": "SITE_ROOT/api/v1/checks/f618072a-7bde-4eee-af63-71a77c5723bc/pause", "ping_url": "PING_ENDPOINTf618072a-7bde-4eee-af63-71a77c5723bc", "status": "new", @@ -426,9 +448,8 @@ curl SITE_ROOT/api/v1/checks/ \ `POST SITE_ROOT/api/v1/checks/` -Updates an existing check. All request parameters are optional. The check is -updated only with the supplied request parameters. If any parameter is omitted, -its value is left unchanged. +Updates an existing check. All request parameters are optional. If you omit any +parameter, SITE_NAME will leave its value unchanged. ### Request Parameters @@ -449,7 +470,7 @@ tags desc : string, optional. - Description for the check. + Description of the check. timeout : number, optional. @@ -474,8 +495,9 @@ schedule A cron expression defining this check's schedule. - If you specify both "timeout" and "schedule" parameters, "timeout" will be - ignored and "schedule" will be used. + If you specify both `timeout` and `schedule` parameters, + SITE_NAME will save the `schedule` parameter and ignore + the `timeout`. Example for a check running every half-hour: @@ -484,7 +506,7 @@ schedule tz : string, optional. - Server's timezone. This setting only has effect in combination with the + Server's timezone. This setting only has an effect in combination with the "schedule" parameter. Example: @@ -494,11 +516,25 @@ tz manual_resume : boolean, optional, default value: false. - Controls whether a paused ping resumes automatically when pinged (the default), + Controls whether a paused ping automatically resumes when pinged (the default), or not. If set to false, a paused check will leave the paused state when it receives - a ping. If set to true, a paused check will ignore pings and stay paused until it is - either manually resumed from the web dashboard or the `manual_resume` flag is - changed. + a ping. If set to true, a paused check will ignore pings and stay paused until + you manually resume it from the web dashboard. + +methods +: string, optional, default value: "". + + Specifies the allowed HTTP methods for making ping requests. + Must be one of the two values: "" (an empty string) or "POST". + + Set this field to "" (an empty string) to allow HEAD, GET, + and POST requests. + + Set this field to "POST" to allow only POST requests. + + Example: + +
{"methods": "POST"}
channels : string, optional. @@ -563,6 +599,7 @@ curl SITE_ROOT/api/v1/checks/f618072a-7bde-4eee-af63-71a77c5723bc \ "name": "Backups", "next_ping": null, "manual_resume": false, + "methods": "", "pause_url": "SITE_ROOT/api/v1/checks/f618072a-7bde-4eee-af63-71a77c5723bc/pause", "ping_url": "PING_ENDPOINTf618072a-7bde-4eee-af63-71a77c5723bc", "status": "new", @@ -618,6 +655,7 @@ header is sometimes required by some network proxies and web servers. "name": "Backups", "next_ping": null, "manual_resume": false, + "methods": "", "pause_url": "SITE_ROOT/api/v1/checks/f618072a-7bde-4eee-af63-71a77c5723bc/pause", "ping_url": "PING_ENDPOINTf618072a-7bde-4eee-af63-71a77c5723bc", "status": "paused", @@ -669,6 +707,7 @@ curl SITE_ROOT/api/v1/checks/f618072a-7bde-4eee-af63-71a77c5723bc \ "name": "Backups", "next_ping": null, "manual_resume": false, + "methods": "", "pause_url": "SITE_ROOT/api/v1/checks/f618072a-7bde-4eee-af63-71a77c5723bc/pause", "ping_url": "PING_ENDPOINTf618072a-7bde-4eee-af63-71a77c5723bc", "status": "new", diff --git a/templates/docs/resources.html b/templates/docs/resources.html index 8d4e6fed..68a2c533 100644 --- a/templates/docs/resources.html +++ b/templates/docs/resources.html @@ -37,4 +37,4 @@ Please submit additions and corrections
  • HealthTray – Watch your healthchecks in Windows system tray.
  • healthchecks/dashboard – A standalone HTML page showing the status of the checks in your account.
  • runitor - A command runner with Healthchecks.io integration to keep your scripts and containers simple.
  • - + \ No newline at end of file