Repository: zulip/zulip

Issue:
Not able to setup intercom integration
Hi, I am using the intercom integration in our self hosted zulip workspace. I am receiving an error :
"Hi there! It looks like you tried to set up the Zulip Intercom integration, but didn't correctly configure the webhook to send data in the JSON format that this integration expects!"

I have checked the webhook from intercom and it seems to be working and sending data in a json format. The following is the ping message format :

```
{
  "data": {
    "item": {
      "type": "ping",
      "message": "This is a ping notification test message."
    },
    "type": "notification_event_data"
  },
  "delivery_attempts": 1,
  "first_sent_at": 1671643827,
  "topic": "ping",
  "created_at": 1671643827,
  "self": null,
  "links": {},
  "id": null,
  "type": "notification_event",
  "app_id": "******",
  "delivered_at": 0,
  "delivery_status": null
}
```
It appears to be a valid json response too


Discussion:
Hello @zulip/server-integrations members, this issue was labeled with the "area: integrations" label, so you may want to check it out!

<!-- areaLabelAddition -->

@PIG208 Would you be up for taking a look at this one?
> @PIG208 Would you be up for taking a look at this one?

OK, got it queued.
This payload is working fine. The error message `Hi there! It looks like you tried to set up the Zulip Intercom integration, but didn't correctly configure the webhook to send data in the JSON format that this integration expects!` only gets sent when the payload is not valid JSON, which doesn't seem to be the case with "ping".

Were you able to get messages for other topics? "ping" messages in particular are ignored by this integration (assuming that the request body is valid JSON). It might be the case that other requests trigger this error message, and the "ping" request gets correctly ignored.

In the documentation, it was mentioned that [Intercom uses a HEAD request to validate the URL](https://developers.intercom.com/building-apps/docs/setting-up-webhooks#configuring-your-endpoint-url), which does trigger the error message here because HEAD is unexpected for the integration.

Resolving patch:
diff --git a/zerver/decorator.py b/zerver/decorator.py
--- a/zerver/decorator.py
+++ b/zerver/decorator.py
@@ -55,7 +55,7 @@
 from zerver.lib.queue import queue_json_publish
 from zerver.lib.rate_limiter import is_local_addr, rate_limit_request_by_ip, rate_limit_user
 from zerver.lib.request import REQ, RequestNotes, has_request_variables
-from zerver.lib.response import json_method_not_allowed, json_success
+from zerver.lib.response import json_method_not_allowed
 from zerver.lib.subdomains import get_subdomain, user_matches_subdomain
 from zerver.lib.timestamp import datetime_to_timestamp, timestamp_to_datetime
 from zerver.lib.users import is_2fa_verified
@@ -962,7 +962,7 @@ def _wrapped_view_func(
         request: HttpRequest, /, *args: ParamT.args, **kwargs: ParamT.kwargs
     ) -> HttpResponse:
         if request.method == "HEAD":
-            return json_success(request)
+            return HttpResponse()
         return view_func(request, *args, **kwargs)
 
     return _wrapped_view_func
diff --git a/zerver/webhooks/intercom/view.py b/zerver/webhooks/intercom/view.py
--- a/zerver/webhooks/intercom/view.py
+++ b/zerver/webhooks/intercom/view.py
@@ -4,7 +4,7 @@
 
 from django.http import HttpRequest, HttpResponse
 
-from zerver.decorator import webhook_view
+from zerver.decorator import return_success_on_head_request, webhook_view
 from zerver.lib.exceptions import UnsupportedWebhookEventTypeError
 from zerver.lib.request import REQ, has_request_variables
 from zerver.lib.response import json_success
@@ -325,6 +325,8 @@ def get_user_unsubscribed_message(payload: WildValue) -> Tuple[str, str]:
 
 
 @webhook_view("Intercom", all_event_types=ALL_EVENT_TYPES)
+# Intercom sends a HEAD request to validate the webhook URL. In this case, we just assume success.
+@return_success_on_head_request
 @has_request_variables
 def api_intercom_webhook(
     request: HttpRequest,


Test patch:
diff --git a/zerver/tests/test_decorators.py b/zerver/tests/test_decorators.py
--- a/zerver/tests/test_decorators.py
+++ b/zerver/tests/test_decorators.py
@@ -1971,8 +1971,8 @@ def test_function(request: HttpRequest) -> HttpResponse:
             return json_response(msg="from_test_function")  # nocoverage. isn't meant to be called
 
         response = test_function(request)
-        self.assert_json_success(response)
-        self.assertNotEqual(orjson.loads(response.content).get("msg"), "from_test_function")
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.content, b"")
 
     def test_returns_normal_response_if_request_method_is_not_head(self) -> None:
         class HeadRequest(HostRequestMock):
diff --git a/zerver/webhooks/intercom/tests.py b/zerver/webhooks/intercom/tests.py
--- a/zerver/webhooks/intercom/tests.py
+++ b/zerver/webhooks/intercom/tests.py
@@ -265,3 +265,7 @@ def test_user_unsubscribed(self) -> None:
             "Contact: Eeshan Garg",
             "User unsubscribed from emails.",
         )
+
+    def test_success_on_http_head(self) -> None:
+        result = self.client_head(self.url)
+        self.assertEqual(result.status_code, 200)