Repository: python-gitlab/python-gitlab

Issue:
Client not parsing json response when "; charset=utf-8 " in `Content-Type` header
## Description of the problem, including code/CLI snippet

When performing a graphql query, with `gitlab.client.http_post` , the result is not parsed as json.

I believe this is because the content type we receive is 
```
      Content-Type:
      - application/json; charset=utf-8
```
while the client check for equality with `application/json` in https://github.com/python-gitlab/python-gitlab/blob/main/gitlab/client.py#L1036

## Expected Behavior

The result is parsed as json

## Actual Behavior

The raw response is returned.

## Specifications

  - python-gitlab version: `python-gitlab==3.13.0`
  - API version you are using (v3/v4): graphql api
  - Gitlab server version (or gitlab.com): 14.10



Discussion:
Hehe @gg-mmill this must be the first time I see someone use the client for graphql like that ;) See also https://github.com/python-gitlab/python-gitlab/issues/1357 for progress there.

Yes, that seems to be it. We should probably get the content type in a more robust way with something like `email.message.Message.get_content_type()` according to
https://peps.python.org/pep-0594/#cgi at least. Thanks for the report!

But please keep in mind that `http_post` was really meant to be part of the REST client. We'll probably create a separate `gitlab.GraphQL` client I assume at some point.
Hello, thanks for the quick reply. We will be monitoring the progress for the graphql client :)

Resolving patch:
diff --git a/gitlab/client.py b/gitlab/client.py
--- a/gitlab/client.py
+++ b/gitlab/client.py
@@ -828,12 +828,9 @@ def http_get(
         result = self.http_request(
             "get", path, query_data=query_data, streamed=streamed, **kwargs
         )
+        content_type = utils.get_content_type(result.headers.get("Content-Type"))
 
-        if (
-            result.headers["Content-Type"] == "application/json"
-            and not streamed
-            and not raw
-        ):
+        if content_type == "application/json" and not streamed and not raw:
             try:
                 json_result = result.json()
                 if TYPE_CHECKING:
@@ -1030,8 +1027,10 @@ def http_post(
             raw=raw,
             **kwargs,
         )
+        content_type = utils.get_content_type(result.headers.get("Content-Type"))
+
         try:
-            if result.headers.get("Content-Type", None) == "application/json":
+            if content_type == "application/json":
                 json_result = result.json()
                 if TYPE_CHECKING:
                     assert isinstance(json_result, dict)
diff --git a/gitlab/utils.py b/gitlab/utils.py
--- a/gitlab/utils.py
+++ b/gitlab/utils.py
@@ -1,3 +1,4 @@
+import email.message
 import pathlib
 import traceback
 import urllib.parse
@@ -14,6 +15,13 @@ def __call__(self, chunk: Any) -> None:
         print(chunk)
 
 
+def get_content_type(content_type: Optional[str]) -> str:
+    message = email.message.Message()
+    message["content-type"] = content_type
+
+    return message.get_content_type()
+
+
 def response_content(
     response: requests.Response,
     streamed: bool,


Test patch:
diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py
--- a/tests/unit/test_utils.py
+++ b/tests/unit/test_utils.py
@@ -8,6 +8,20 @@
 from gitlab import types, utils
 
 
+@pytest.mark.parametrize(
+    "content_type,expected_type",
+    [
+        ("application/json", "application/json"),
+        ("application/json; charset=utf-8", "application/json"),
+        ("", "text/plain"),
+        (None, "text/plain"),
+    ],
+)
+def test_get_content_type(content_type, expected_type):
+    parsed_type = utils.get_content_type(content_type)
+    assert parsed_type == expected_type
+
+
 @responses.activate
 def test_response_content(capsys):
     responses.add(