From bd12d38ee049bd70060de699a12bf08b97098065 Mon Sep 17 00:00:00 2001 From: Vadim Stepanov Date: Thu, 16 Mar 2023 09:40:50 +0000 Subject: [PATCH] Public API: allow `null` escalation chain when creating routes (#1557) # What this PR does Allows passing `null` as a value for `escalation_chain` when creating routes via the public API. ## Which issue(s) this PR fixes This is needed to unblock https://github.com/grafana/oncall/pull/1555 + creating a route without an escalation chain is possible in the web UI, so this PR makes the public API more consistent with the web UI. ## Checklist - [x] Tests updated - [x] Documentation added - [x] `CHANGELOG.md` updated --- CHANGELOG.md | 6 ++++ docs/sources/oncall-api-reference/routes.md | 2 +- engine/apps/public_api/serializers/routes.py | 1 + engine/apps/public_api/tests/test_routes.py | 31 ++++++++++++++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfb8758c..a7562498 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed + +- Allow passing `null` as a value for `escalation_chain` when creating routes via the public API ([1557](https://github.com/grafana/oncall/pull/1557)) + ## v1.1.39 (2023-03-16) ### Added diff --git a/docs/sources/oncall-api-reference/routes.md b/docs/sources/oncall-api-reference/routes.md index 3ffa32e9..49dfc25a 100644 --- a/docs/sources/oncall-api-reference/routes.md +++ b/docs/sources/oncall-api-reference/routes.md @@ -51,7 +51,7 @@ Routes allow you to direct different alerts to different messenger channels and | Parameter | Unique | Required | Description | |-----------------------| :----: |:--------:|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `integration_id` | No | Yes | Each route is assigned to a specific integration. | -| `escalation_chain_id` | No | Yes | Each route is assigned a specific escalation chain. | +| `escalation_chain_id` | No | Yes | Each route is assigned a specific escalation chain. Explicitly pass `null` to create a route without an escalation chain assigned. | | `routing_type` | Yes | No | Routing type that can be either `jinja2` or `regex`(default value) | | `routing_regex` | Yes | Yes | Jinja2 template or Python Regex query (use for debugging). OnCall chooses the route for an alert in case there is a match inside the whole alert payload. | | `position` | Yes | Optional | Route matching is performed one after another starting from position=`0`. Position=`-1` will put the route to the end of the list before `is_the_last_route`. A new route created with a position of an existing route will move the old route (and all following routes) down in the list. | diff --git a/engine/apps/public_api/serializers/routes.py b/engine/apps/public_api/serializers/routes.py index abbc9f4f..07d2b398 100644 --- a/engine/apps/public_api/serializers/routes.py +++ b/engine/apps/public_api/serializers/routes.py @@ -155,6 +155,7 @@ class ChannelFilterSerializer(BaseChannelFilterSerializer): escalation_chain_id = OrganizationFilteredPrimaryKeyRelatedField( queryset=EscalationChain.objects, source="escalation_chain", + allow_null=True, ) is_the_last_route = serializers.BooleanField(read_only=True, source="is_default") diff --git a/engine/apps/public_api/tests/test_routes.py b/engine/apps/public_api/tests/test_routes.py index 5f0db22d..300a943a 100644 --- a/engine/apps/public_api/tests/test_routes.py +++ b/engine/apps/public_api/tests/test_routes.py @@ -162,6 +162,37 @@ def test_create_route( assert response.json() == expected_response +@pytest.mark.django_db +def test_create_route_without_escalation_chain(route_public_api_setup): + _, _, token, alert_receive_channel, escalation_chain, _ = route_public_api_setup + + client = APIClient() + url = reverse("api-public:routes-list") + + data = { + "integration_id": alert_receive_channel.public_primary_key, + "routing_regex": "testreg", + "escalation_chain_id": None, + } + response = client.post(url, format="json", HTTP_AUTHORIZATION=token, data=data) + + expected_response = { + "id": response.data["id"], + "integration_id": alert_receive_channel.public_primary_key, + "escalation_chain_id": None, + "routing_type": "regex", + "routing_regex": data["routing_regex"], + "position": 0, + "is_the_last_route": False, + "slack": {"channel_id": None, "enabled": True}, + "telegram": {"id": None, "enabled": False}, + TEST_MESSAGING_BACKEND_FIELD: {"id": None, "enabled": False}, + } + + assert response.status_code == status.HTTP_201_CREATED + assert response.json() == expected_response + + @pytest.mark.django_db def test_invalid_route_data( route_public_api_setup,