Repository: saleor/saleor

Issue:
Order creation webhook doesn't send data with "application/json"
### What I'm trying to achieve
Send order data to a web server via webhooks

### Steps to reproduce the problem
1. Create a simple server that prints out the JSON request body
2. Create a order creation webhook pointing to this server
3. Create a new order
4. Observe that the JSON request body sent to the server is empty

### What I expected to happen
Order data should be sent as JSON body

### Screenshots
<img width="409" alt="image" src="https://user-images.githubusercontent.com/20501235/85254204-4db2a680-b492-11ea-9272-c5a51cd69b98.png">

Second line is `console.log(req.body)` in an example expressjs server with `body-parser` JSON parser

After modifying the file in `saleor/plugins/webhook/tasks.py` to add the `Content-Type: application/json` header when sending the data, it worked as expected.


Discussion:
Looks like it, we send no content-type headers. https://github.com/mirumee/saleor/blob/0e7267bb26405100fbbcd6864c4123139129c62d/saleor/plugins/webhook/tasks.py#L47-L50

cc. @korycins  

Resolving patch:
diff --git a/saleor/plugins/webhook/tasks.py b/saleor/plugins/webhook/tasks.py
--- a/saleor/plugins/webhook/tasks.py
+++ b/saleor/plugins/webhook/tasks.py
@@ -46,7 +46,7 @@ def trigger_webhooks_for_event(event_type, data):
 def send_webhook_request(webhook_id, target_url, secret, event_type, data):
     headers = create_webhook_headers(event_type, data, secret)
     response = requests.post(
-        target_url, data=data, headers=headers, timeout=WEBHOOK_TIMEOUT
+        target_url, json=data, headers=headers, timeout=WEBHOOK_TIMEOUT
     )
     response.raise_for_status()
     logger.debug(


Test patch:
diff --git a/saleor/plugins/webhook/tests/cassettes/test_trigger_webhooks_for_event.yaml b/saleor/plugins/webhook/tests/cassettes/test_trigger_webhooks_for_event.yaml
--- a/saleor/plugins/webhook/tests/cassettes/test_trigger_webhooks_for_event.yaml
+++ b/saleor/plugins/webhook/tests/cassettes/test_trigger_webhooks_for_event.yaml
@@ -1,14 +1,16 @@
 interactions:
 - request:
-    body: '[{"model": "order.order", "pk": 84, "fields": {"created": "2019-09-17T13:54:55.227Z",
-      "status": "unfulfilled", "user": 92, "language_code": "en", "tracking_client_id":
-      "", "billing_address": 351, "shipping_address": 352, "user_email": "test@example.com",
-      "currency": "USD", "shipping_method": 84, "shipping_method_name": "DHL", "shipping_price_net_amount":
-      "10.00", "shipping_price_gross_amount": "12.30", "token": "ce7b260c-f70d-430b-b88b-3519624a7974",
-      "checkout_token": "", "total_net_amount": "80.00", "total_gross_amount": "98.40",
-      "voucher": null, "discount_amount": "0.00", "discount_name": "", "translated_discount_name":
-      "", "display_gross_prices": true, "customer_note": "", "weight": "0.0 kg", "gift_cards":
-      []}}]'
+    body: '"[{\"model\": \"order.order\", \"pk\": 2123, \"fields\": {\"private_metadata\":
+      {}, \"metadata\": {}, \"created\": \"2020-06-26T09:24:25.891Z\", \"status\":
+      \"unfulfilled\", \"user\": 7766, \"language_code\": \"en\", \"tracking_client_id\":
+      \"\", \"billing_address\": 9742, \"shipping_address\": 9743, \"user_email\":
+      \"test@example.com\", \"currency\": \"USD\", \"shipping_method\": 3776, \"shipping_method_name\":
+      \"DHL\", \"shipping_price_net_amount\": \"10.00\", \"shipping_price_gross_amount\":
+      \"12.30\", \"token\": \"e23b64b3-e16d-4ca8-b637-30122e5ef07d\", \"checkout_token\":
+      \"\", \"total_net_amount\": \"80.00\", \"total_gross_amount\": \"98.40\", \"voucher\":
+      null, \"discount_amount\": \"0.00\", \"discount_name\": null, \"translated_discount_name\":
+      null, \"display_gross_prices\": true, \"customer_note\": \"\", \"weight\": \"0.0:kg\",
+      \"gift_cards\": []}}]"'
     headers:
       Accept:
       - '*/*'
@@ -17,15 +19,17 @@ interactions:
       Connection:
       - keep-alive
       Content-Length:
-      - '724'
+      - '872'
+      Content-Type:
+      - application/json
       User-Agent:
-      - python-requests/2.22.0
+      - python-requests/2.24.0
       X-Saleor-Domain:
       - mirumee.com
       X-Saleor-Event:
       - order_created
     method: POST
-    uri: https://webhook.site/f0fc9979-cbd4-47b7-8705-1acb03fff1d0
+    uri: https://webhook.site/82aa4765-6e5b-4478-b1e1-120a8c9d6668
   response:
     body:
       string: !!binary |
@@ -38,21 +42,20 @@ interactions:
       Content-Type:
       - text/plain; charset=UTF-8
       Date:
-      - Tue, 17 Sep 2019 13:54:59 GMT
+      - Fri, 26 Jun 2020 09:24:26 GMT
       Server:
-      - nginx/1.10.3
+      - nginx/1.14.2
+      Set-Cookie:
+      - laravel_session=bPrz80wlcnUh2VU35r3HfWkgdk6WX0bwu8KIKuR4; expires=Fri, 26-Jun-2020
+        11:24:26 GMT; Max-Age=7200; path=/; httponly
       Transfer-Encoding:
       - chunked
       Vary:
       - Accept-Encoding
-      X-RateLimit-Limit:
-      - '30'
-      X-RateLimit-Remaining:
-      - '29'
       X-Request-Id:
-      - 33161af9-c306-4918-ad9b-fad2b5b9897f
+      - be89fde5-febc-41fc-bd83-14b565abd20e
       X-Token-Id:
-      - f0fc9979-cbd4-47b7-8705-1acb03fff1d0
+      - 82aa4765-6e5b-4478-b1e1-120a8c9d6668
     status:
       code: 200
       message: OK
diff --git a/saleor/plugins/webhook/tests/cassettes/test_trigger_webhooks_for_event_with_secret_key.yaml b/saleor/plugins/webhook/tests/cassettes/test_trigger_webhooks_for_event_with_secret_key.yaml
--- a/saleor/plugins/webhook/tests/cassettes/test_trigger_webhooks_for_event_with_secret_key.yaml
+++ b/saleor/plugins/webhook/tests/cassettes/test_trigger_webhooks_for_event_with_secret_key.yaml
@@ -1,14 +1,16 @@
 interactions:
 - request:
-    body: '[{"model": "order.order", "pk": 85, "fields": {"created": "2019-09-17T13:55:00.075Z",
-      "status": "unfulfilled", "user": 93, "language_code": "en", "tracking_client_id":
-      "", "billing_address": 355, "shipping_address": 356, "user_email": "test@example.com",
-      "currency": "USD", "shipping_method": 85, "shipping_method_name": "DHL", "shipping_price_net_amount":
-      "10.00", "shipping_price_gross_amount": "12.30", "token": "c71a5c72-7d0a-4505-ac1e-217b877fce24",
-      "checkout_token": "", "total_net_amount": "80.00", "total_gross_amount": "98.40",
-      "voucher": null, "discount_amount": "0.00", "discount_name": "", "translated_discount_name":
-      "", "display_gross_prices": true, "customer_note": "", "weight": "0.0 kg", "gift_cards":
-      []}}]'
+    body: '"[{\"model\": \"order.order\", \"pk\": 3172, \"fields\": {\"private_metadata\":
+      {}, \"metadata\": {}, \"created\": \"2020-06-26T09:24:25.741Z\", \"status\":
+      \"unfulfilled\", \"user\": 8007, \"language_code\": \"en\", \"tracking_client_id\":
+      \"\", \"billing_address\": 11138, \"shipping_address\": 11139, \"user_email\":
+      \"test@example.com\", \"currency\": \"USD\", \"shipping_method\": 3827, \"shipping_method_name\":
+      \"DHL\", \"shipping_price_net_amount\": \"10.00\", \"shipping_price_gross_amount\":
+      \"12.30\", \"token\": \"7c948b73-e748-4818-9e8e-e00858bc224b\", \"checkout_token\":
+      \"\", \"total_net_amount\": \"80.00\", \"total_gross_amount\": \"98.40\", \"voucher\":
+      null, \"discount_amount\": \"0.00\", \"discount_name\": null, \"translated_discount_name\":
+      null, \"display_gross_prices\": true, \"customer_note\": \"\", \"weight\": \"0.0:kg\",
+      \"gift_cards\": []}}]"'
     headers:
       Accept:
       - '*/*'
@@ -17,17 +19,19 @@ interactions:
       Connection:
       - keep-alive
       Content-Length:
-      - '724'
+      - '874'
+      Content-Type:
+      - application/json
       User-Agent:
-      - python-requests/2.22.0
+      - python-requests/2.24.0
       X-Saleor-Domain:
       - mirumee.com
       X-Saleor-Event:
       - order_created
       X-Saleor-HMAC-SHA256:
-      - sha1=43c77ab2a62add6e6a18dbf3346f87c55c4e50aefebc39f31160764686ed9048
+      - sha1=0758d80e52e9eafa35bfc83039ba2e4ad43487c5233ef10136709d01b61fbabb
     method: POST
-    uri: https://webhook.site/f0fc9979-cbd4-47b7-8705-1acb03fff1d0
+    uri: https://webhook.site/82aa4765-6e5b-4478-b1e1-120a8c9d6668
   response:
     body:
       string: !!binary |
@@ -40,21 +44,20 @@ interactions:
       Content-Type:
       - text/plain; charset=UTF-8
       Date:
-      - Tue, 17 Sep 2019 13:55:02 GMT
+      - Fri, 26 Jun 2020 09:24:26 GMT
       Server:
-      - nginx/1.10.3
+      - nginx/1.14.2
+      Set-Cookie:
+      - laravel_session=qB9DkYxEQwlzPYStZmaEH39iIiAiH8IsA3OMr9wt; expires=Fri, 26-Jun-2020
+        11:24:26 GMT; Max-Age=7200; path=/; httponly
       Transfer-Encoding:
       - chunked
       Vary:
       - Accept-Encoding
-      X-RateLimit-Limit:
-      - '30'
-      X-RateLimit-Remaining:
-      - '28'
       X-Request-Id:
-      - a731d727-0404-4bb9-b3d0-c0ba40491bce
+      - ae821cbd-9fc0-499e-bed4-ead276fb464f
       X-Token-Id:
-      - f0fc9979-cbd4-47b7-8705-1acb03fff1d0
+      - 82aa4765-6e5b-4478-b1e1-120a8c9d6668
     status:
       code: 200
       message: OK
diff --git a/saleor/plugins/webhook/tests/test_webhook.py b/saleor/plugins/webhook/tests/test_webhook.py
--- a/saleor/plugins/webhook/tests/test_webhook.py
+++ b/saleor/plugins/webhook/tests/test_webhook.py
@@ -28,7 +28,7 @@ def test_trigger_webhooks_for_event(
     permission_manage_products,
 ):
     webhook.app.permissions.add(permission_manage_orders)
-    webhook.target_url = "https://webhook.site/f0fc9979-cbd4-47b7-8705-1acb03fff1d0"
+    webhook.target_url = "https://webhook.site/82aa4765-6e5b-4478-b1e1-120a8c9d6668"
     webhook.save()
 
     expected_data = serialize("json", [order_with_lines])
@@ -41,7 +41,7 @@ def test_trigger_webhooks_for_event(
     }
 
     mock_request.assert_called_once_with(
-        webhook.target_url, data=expected_data, headers=expected_headers, timeout=10
+        webhook.target_url, json=expected_data, headers=expected_headers, timeout=10
     )
 
 
@@ -111,7 +111,7 @@ def test_trigger_webhooks_for_event_with_secret_key(
     mock_request, webhook, order_with_lines, permission_manage_orders
 ):
     webhook.app.permissions.add(permission_manage_orders)
-    webhook.target_url = "https://webhook.site/f0fc9979-cbd4-47b7-8705-1acb03fff1d0"
+    webhook.target_url = "https://webhook.site/82aa4765-6e5b-4478-b1e1-120a8c9d6668"
     webhook.secret_key = "secret_key"
     webhook.save()
 
@@ -128,7 +128,7 @@ def test_trigger_webhooks_for_event_with_secret_key(
     }
 
     mock_request.assert_called_once_with(
-        webhook.target_url, data=expected_data, headers=expected_headers, timeout=10
+        webhook.target_url, json=expected_data, headers=expected_headers, timeout=10
     )