Repository: saleor/saleor

Issue:
json serialization error with google analytics enabled
### What I'm trying to achieve
I want to enable google analytics of a docker image I build using the current docker files from the master repo. I am starting it in beanstalk and then the error occurs:  
```
/var/log/eb-docker/containers/eb-current-app/eb-d5c249ad18da-stdouterr.log
-------------------------------------
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type generator is not JSON serializable

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "./saleor/core/middleware.py", line 27, in middleware
    client_id, path=path, language=language, headers=headers)
  File "./saleor/core/analytics.py", line 68, in report_view
    _report(client_id, payloads, extra_headers=extra_headers)
  File "./saleor/core/analytics.py", line 36, in _report
    **extra_data)
  File "/usr/local/lib/python3.7/site-packages/celery/app/task.py", line 427, in delay
    return self.apply_async(args, kwargs)
  File "/usr/local/lib/python3.7/site-packages/celery/app/task.py", line 544, in apply_async
    body, serializer,
  File "/usr/local/lib/python3.7/site-packages/kombu/serialization.py", line 221, in dumps
    payload = encoder(data)
  File "/usr/local/lib/python3.7/contextlib.py", line 130, in __exit__
    self.gen.throw(type, value, traceback)
  File "/usr/local/lib/python3.7/site-packages/kombu/serialization.py", line 54, in _reraise_errors
    reraise(wrapper, wrapper(exc), sys.exc_info()[2])
  File "/usr/local/lib/python3.7/site-packages/vine/five.py", line 194, in reraise
    raise value.with_traceback(tb)
  File "/usr/local/lib/python3.7/site-packages/kombu/serialization.py", line 50, in _reraise_errors
    yield
  File "/usr/local/lib/python3.7/site-packages/kombu/serialization.py", line 221, in dumps
    payload = encoder(data)
  File "/usr/local/lib/python3.7/site-packages/kombu/utils/json.py", line 69, in dumps
    **dict(default_kwargs, **kwargs))
  File "/usr/local/lib/python3.7/json/__init__.py", line 238, in dumps
    **kw).encode(obj)
  File "/usr/local/lib/python3.7/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/local/lib/python3.7/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/usr/local/lib/python3.7/site-packages/kombu/utils/json.py", line 59, in default
    return super(JSONEncoder, self).default(o)
  File "/usr/local/lib/python3.7/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
kombu.exceptions.EncodeError: Object of type generator is not JSON serializable
ERROR saleor.core.middleware Unable to update analytics [PID:17:uWSGIWorker2Core0]
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/kombu/serialization.py", line 50, in _reraise_errors
    yield
  File "/usr/local/lib/python3.7/site-packages/kombu/serialization.py", line 221, in dumps
    payload = encoder(data)
  File "/usr/local/lib/python3.7/site-packages/kombu/utils/json.py", line 69, in dumps
    **dict(default_kwargs, **kwargs))
  File "/usr/local/lib/python3.7/json/__init__.py", line 238, in dumps
    **kw).encode(obj)
  File "/usr/local/lib/python3.7/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/local/lib/python3.7/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/usr/local/lib/python3.7/site-packages/kombu/utils/json.py", line 59, in default
    return super(JSONEncoder, self).default(o)
  File "/usr/local/lib/python3.7/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type generator is not JSON serializable

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "./saleor/core/middleware.py", line 27, in middleware
    client_id, path=path, language=language, headers=headers)
  File "./saleor/core/analytics.py", line 68, in report_view
    _report(client_id, payloads, extra_headers=extra_headers)
  File "./saleor/core/analytics.py", line 36, in _report
    **extra_data)
  File "/usr/local/lib/python3.7/site-packages/celery/app/task.py", line 427, in delay
    return self.apply_async(args, kwargs)
  File "/usr/local/lib/python3.7/site-packages/celery/app/task.py", line 544, in apply_async
    body, serializer,
  File "/usr/local/lib/python3.7/site-packages/kombu/serialization.py", line 221, in dumps
    payload = encoder(data)
  File "/usr/local/lib/python3.7/contextlib.py", line 130, in __exit__
    self.gen.throw(type, value, traceback)
  File "/usr/local/lib/python3.7/site-packages/kombu/serialization.py", line 54, in _reraise_errors
    reraise(wrapper, wrapper(exc), sys.exc_info()[2])
  File "/usr/local/lib/python3.7/site-packages/vine/five.py", line 194, in reraise
    raise value.with_traceback(tb)
  File "/usr/local/lib/python3.7/site-packages/kombu/serialization.py", line 50, in _reraise_errors
    yield
  File "/usr/local/lib/python3.7/site-packages/kombu/serialization.py", line 221, in dumps
    payload = encoder(data)
  File "/usr/local/lib/python3.7/site-packages/kombu/utils/json.py", line 69, in dumps
    **dict(default_kwargs, **kwargs))
  File "/usr/local/lib/python3.7/json/__init__.py", line 238, in dumps
    **kw).encode(obj)
  File "/usr/local/lib/python3.7/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/local/lib/python3.7/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/usr/local/lib/python3.7/site-packages/kombu/utils/json.py", line 59, in default
    return super(JSONEncoder, self).default(o)
  File "/usr/local/lib/python3.7/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
kombu.exceptions.EncodeError: Object of type generator is not JSON serializable
UWSGI uwsgi "GET /de/cart/summary/ HTTP/1.1" 200 785 29ms [PID:17:Worker-2] [RSS:162MB]```

### Steps to reproduce the problem
1. Build the docker image  
2. Try to start it in beanstalk with the minimal needed env variables set and google analytics id set too  
3. Look into the logs and find the error in question

### What I expected to happen
Beanstalk starts the dockerimage with the needed minimal env variables and google analytics enabled.  
  
There is a merge request related to this #3615  


Discussion:
This commit: https://github.com/mirumee/saleor/pull/3615/files fixed this issue
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

I think none of us was able to reproduce this issue, but is seems some people are still getting it. Maybe we should revert this change linked above if it doesn't break anything else now.
> This commit: https://github.com/mirumee/saleor/pull/3615/files fixed this issue

I resolved the issue using this.

Resolving patch:
diff --git a/saleor/core/analytics.py b/saleor/core/analytics.py
--- a/saleor/core/analytics.py
+++ b/saleor/core/analytics.py
@@ -1,4 +1,5 @@
 import uuid
+from typing import Generator, List
 
 import google_measurement_protocol as ga
 from django.conf import settings
@@ -23,17 +24,25 @@ def get_client_id(request):
 
 
 @app.task
-def ga_report(tracking_id, client_id, payloads, extra_headers=None, **extra_data):
+def ga_report(
+    tracking_id, client_id, payloads: List[dict], extra_headers=None, **extra_data
+):
     ga.report(
         tracking_id, client_id, payloads, extra_headers=extra_headers, **extra_data
     )
 
 
-def _report(client_id, payloads, extra_headers=None, **extra_data):
+def _report(
+    client_id, payloads: Generator[dict, None, None], extra_headers=None, **extra_data
+):
     tracking_id = getattr(settings, "GOOGLE_ANALYTICS_TRACKING_ID", None)
     if tracking_id and client_id:
         ga_report.delay(
-            tracking_id, client_id, payloads, extra_headers=extra_headers, **extra_data
+            tracking_id,
+            client_id,
+            list(payloads),
+            extra_headers=extra_headers,
+            **extra_data,
         )
 
 


Test patch:
diff --git a/tests/test_analytics.py b/tests/test_analytics.py
--- a/tests/test_analytics.py
+++ b/tests/test_analytics.py
@@ -1,11 +1,7 @@
 from decimal import Decimal
+from unittest import mock
 
-from saleor.core.analytics import (
-    get_order_payloads,
-    get_view_payloads,
-    report_order,
-    report_view,
-)
+from saleor.core.analytics import get_order_payloads, report_order, report_view
 
 
 def test_get_order_payloads(order_with_lines):
@@ -32,20 +28,27 @@ def test_get_order_payloads(order_with_lines):
         assert Decimal(item["ip"]) == line.unit_price.gross.amount
 
 
-def test_report_order_has_no_errors(order_with_lines):
-    report_order("", order_with_lines)
+@mock.patch("google_measurement_protocol.report")
+def test_report_order_has_no_errors(mocked_ga_report, order_with_lines, settings):
+    settings.GOOGLE_ANALYTICS_TRACKING_ID = "ga_id"
+    report_order("dummy_client_id", order_with_lines)
+    mocked_ga_report.assert_called_once()
 
 
-def test_get_view_payloads():
+@mock.patch("google_measurement_protocol.report")
+def test_get_view_payloads(mocked_ga_report, settings):
+    settings.GOOGLE_ANALYTICS_TRACKING_ID = "ga_id"
     headers = {"HTTP_HOST": "getsaleor.com", "HTTP_REFERER": "example.com"}
-    generator = get_view_payloads("/test-path/", "en-us", headers)
-    data = list(generator)[0]
-    assert data["dp"] == "/test-path/"
-    assert data["dh"] == "getsaleor.com"
-    assert data["dr"] == "example.com"
-    assert data["ul"] == "en-us"
-
-
-def test_report_view_has_no_errors():
-    headers = {"HTTP_HOST": "getsaleor.com", "HTTP_REFERER": "example.com"}
-    report_view("", "/test-path/", "en-us", headers)
+    report_view("dummy_client_id", "/test-path/", "en-us", headers)
+    expected_payload = [
+        {
+            "t": "pageview",
+            "dp": "/test-path/",
+            "dh": "getsaleor.com",
+            "dr": "example.com",
+            "ul": "en-us",
+        }
+    ]
+    mocked_ga_report.assert_called_once_with(
+        "ga_id", "dummy_client_id", expected_payload, extra_headers={}
+    )