diff --git a/CHANGELOG.md b/CHANGELOG.md index f650206dab..caf2012c22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Allow mobile app to consume "internal" schedules API endpoints by @joeyorlando ([#2109](https://github.com/grafana/oncall/pull/2109)) +- Add inbound email address in integration API by @vadimkerr ([#2113](https://github.com/grafana/oncall/pull/2113)) ## v1.2.39 (2023-06-06) diff --git a/docs/sources/oncall-api-reference/integrations.md b/docs/sources/oncall-api-reference/integrations.md index 00f4702c47..e67ddf135a 100644 --- a/docs/sources/oncall-api-reference/integrations.md +++ b/docs/sources/oncall-api-reference/integrations.md @@ -24,6 +24,7 @@ The above command returns JSON structured in the following way: "name": "Grafana :blush:", "team_id": null, "link": "{{API_URL}}/integrations/v1/grafana/mReAoNwDm0eMwKo1mTeTwYo/", + "inbound_email": null, "type": "grafana", "default_route": { "id": "RVBE4RKQSCGJ2", @@ -96,6 +97,7 @@ The above command returns JSON structured in the following way: "name": "Grafana :blush:", "team_id": null, "link": "{{API_URL}}/integrations/v1/grafana/mReAoNwDm0eMwKo1mTeTwYo/", + "inbound_email": null, "type": "grafana", "default_route": { "id": "RVBE4RKQSCGJ2", @@ -171,6 +173,7 @@ The above command returns JSON structured in the following way: "name": "Grafana :blush:", "team_id": null, "link": "{{API_URL}}/integrations/v1/grafana/mReAoNwDm0eMwKo1mTeTwYo/", + "inbound_email": null, "type": "grafana", "default_route": { "id": "RVBE4RKQSCGJ2", @@ -252,6 +255,7 @@ The above command returns JSON structured in the following way: "name": "Grafana :blush:", "team_id": null, "link": "{{API_URL}}/integrations/v1/grafana/mReAoNwDm0eMwKo1mTeTwYo/", + "inbound_email": null, "type": "grafana", "default_route": { "id": "RVBE4RKQSCGJ2", diff --git a/engine/apps/alerts/models/alert_receive_channel.py b/engine/apps/alerts/models/alert_receive_channel.py index 7c0d27915b..81a0286e89 100644 --- a/engine/apps/alerts/models/alert_receive_channel.py +++ b/engine/apps/alerts/models/alert_receive_channel.py @@ -398,6 +398,9 @@ def integration_url(self): @property def inbound_email(self): + if self.integration != AlertReceiveChannel.INTEGRATION_INBOUND_EMAIL: + return None + return f"{self.token}@{live_settings.INBOUND_EMAIL_DOMAIN}" @property diff --git a/engine/apps/public_api/serializers/integrations.py b/engine/apps/public_api/serializers/integrations.py index dea69ef719..b9ee74273f 100644 --- a/engine/apps/public_api/serializers/integrations.py +++ b/engine/apps/public_api/serializers/integrations.py @@ -76,6 +76,7 @@ class IntegrationSerializer(EagerLoadingMixin, serializers.ModelSerializer, Main name = serializers.CharField(required=False, source="verbal_name") team_id = TeamPrimaryKeyRelatedField(required=False, allow_null=True, source="team") link = serializers.ReadOnlyField(source="integration_url") + inbound_email = serializers.ReadOnlyField() type = IntegrationTypeField(source="integration") templates = serializers.DictField(required=False) default_route = serializers.DictField(required=False) @@ -93,6 +94,7 @@ class Meta: "description_short", "team_id", "link", + "inbound_email", "type", "default_route", "templates", diff --git a/engine/apps/public_api/tests/test_integrations.py b/engine/apps/public_api/tests/test_integrations.py index 6bbea4ae0d..3a4a2db38d 100644 --- a/engine/apps/public_api/tests/test_integrations.py +++ b/engine/apps/public_api/tests/test_integrations.py @@ -33,6 +33,7 @@ def test_get_list_integrations( "name": "grafana", "description_short": "Some description", "link": integration.integration_url, + "inbound_email": None, "type": "grafana", "default_route": { "escalation_chain_id": None, @@ -165,6 +166,7 @@ def test_update_integration_template( "name": "grafana", "description_short": None, "link": integration.integration_url, + "inbound_email": None, "type": "grafana", "default_route": { "escalation_chain_id": None, @@ -227,6 +229,7 @@ def test_update_integration_template_messaging_backend( "name": "grafana", "description_short": None, "link": integration.integration_url, + "inbound_email": None, "type": "grafana", "default_route": { "escalation_chain_id": None, @@ -305,6 +308,7 @@ def test_update_resolve_signal_template( "name": "grafana", "description_short": None, "link": integration.integration_url, + "inbound_email": None, "type": "grafana", "default_route": { "escalation_chain_id": None, @@ -415,6 +419,7 @@ def test_update_sms_template_with_empty_dict( "name": "grafana", "description_short": None, "link": integration.integration_url, + "inbound_email": None, "type": "grafana", "default_route": { "escalation_chain_id": None, @@ -477,6 +482,7 @@ def test_update_integration_name( "name": "grafana_updated", "description_short": None, "link": integration.integration_url, + "inbound_email": None, "type": "grafana", "default_route": { "escalation_chain_id": None, @@ -539,6 +545,7 @@ def test_update_integration_name_and_description_short( "name": "grafana_updated", "description_short": "Some description", "link": integration.integration_url, + "inbound_email": None, "type": "grafana", "default_route": { "escalation_chain_id": None, @@ -604,6 +611,7 @@ def test_set_default_template( "name": "grafana", "description_short": None, "link": integration.integration_url, + "inbound_email": None, "type": "grafana", "default_route": { "escalation_chain_id": None, @@ -672,6 +680,7 @@ def test_set_default_messaging_backend_template( "name": "grafana", "description_short": None, "link": integration.integration_url, + "inbound_email": None, "type": "grafana", "default_route": { "escalation_chain_id": None, @@ -734,3 +743,51 @@ def test_get_list_integrations_direct_paging_hidden( # Check no direct paging integrations in the response assert response.status_code == status.HTTP_200_OK assert response.json()["results"] == [] + + +@pytest.mark.django_db +def test_get_list_integrations_link_and_inbound_email( + make_organization_and_user_with_token, + make_alert_receive_channel, + make_channel_filter, + make_integration_heartbeat, + settings, +): + """ + Check that "link" and "inbound_email" fields are populated correctly for different integration types. + """ + + settings.BASE_URL = "https://test.com" + settings.INBOUND_EMAIL_DOMAIN = "test.com" + + organization, user, token = make_organization_and_user_with_token() + + for integration in AlertReceiveChannel._config: + make_alert_receive_channel(organization, integration=integration.slug, token="test123") + + client = APIClient() + url = reverse("api-public:integrations-list") + + response = client.get(url, HTTP_AUTHORIZATION=f"{token}") + assert response.status_code == status.HTTP_200_OK + + for integration in response.json()["results"]: + integration_type, integration_link, integration_inbound_email = ( + integration["type"], + integration["link"], + integration["inbound_email"], + ) + + if integration_type in [ + AlertReceiveChannel.INTEGRATION_MANUAL, + AlertReceiveChannel.INTEGRATION_SLACK_CHANNEL, + AlertReceiveChannel.INTEGRATION_MAINTENANCE, + ]: + assert integration_link is None + assert integration_inbound_email is None + elif integration_type == AlertReceiveChannel.INTEGRATION_INBOUND_EMAIL: + assert integration_link is None + assert integration_inbound_email == "test123@test.com" + else: + assert integration_link == f"https://test.com/integrations/v1/{integration_type}/test123/" + assert integration_inbound_email is None