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
This commit is contained in:
Vadim Stepanov 2023-03-16 09:40:50 +00:00 committed by GitHub
parent 4bb6397cf1
commit bd12d38ee0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 39 additions and 1 deletions

View file

@ -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/), 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). 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) ## v1.1.39 (2023-03-16)
### Added ### Added

View file

@ -51,7 +51,7 @@ Routes allow you to direct different alerts to different messenger channels and
| Parameter | Unique | Required | Description | | Parameter | Unique | Required | Description |
|-----------------------| :----: |:--------:|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |-----------------------| :----: |:--------:|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `integration_id` | No | Yes | Each route is assigned to a specific integration. | | `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_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 <https://regex101.com/> for debugging). OnCall chooses the route for an alert in case there is a match inside the whole alert payload. | | `routing_regex` | Yes | Yes | Jinja2 template or Python Regex query (use <https://regex101.com/> 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. | | `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. |

View file

@ -155,6 +155,7 @@ class ChannelFilterSerializer(BaseChannelFilterSerializer):
escalation_chain_id = OrganizationFilteredPrimaryKeyRelatedField( escalation_chain_id = OrganizationFilteredPrimaryKeyRelatedField(
queryset=EscalationChain.objects, queryset=EscalationChain.objects,
source="escalation_chain", source="escalation_chain",
allow_null=True,
) )
is_the_last_route = serializers.BooleanField(read_only=True, source="is_default") is_the_last_route = serializers.BooleanField(read_only=True, source="is_default")

View file

@ -162,6 +162,37 @@ def test_create_route(
assert response.json() == expected_response 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 @pytest.mark.django_db
def test_invalid_route_data( def test_invalid_route_data(
route_public_api_setup, route_public_api_setup,