Coverage for src\baobab_web_api_caller\core\json_response_decoder.py: 79%

33 statements  

« prev     ^ index     » next       coverage.py v7.10.3, created at 2026-03-21 12:10 +0100

1"""Décodage JSON des réponses.""" 

2 

3from __future__ import annotations 

4 

5import json 

6from dataclasses import dataclass 

7 

8from baobab_web_api_caller.core.baobab_response import BaobabResponse 

9from baobab_web_api_caller.core.response_decoder import ResponseDecoder 

10from baobab_web_api_caller.exceptions.response_decoding_exception import ResponseDecodingException 

11 

12 

13@dataclass(frozen=True, slots=True) 

14class JsonResponseDecoder(ResponseDecoder): 

15 """Décode le JSON à partir du corps texte/binaire. 

16 

17 Le décodage est tenté uniquement si l'en-tête ``Content-Type`` indique un JSON 

18 (``application/json`` ou un type ``application/*+json``). 

19 """ 

20 

21 def decode(self, response: BaobabResponse) -> BaobabResponse: 

22 content_type = response.headers.get("Content-Type", "") 

23 if not self._is_json_content_type(content_type): 

24 return response 

25 

26 if response.text is not None: 26 ↛ 28line 26 didn't jump to line 28 because the condition on line 26 was always true

27 raw_text = response.text 

28 elif response.content is not None: 

29 try: 

30 raw_text = response.content.decode("utf-8") 

31 except UnicodeDecodeError as exc: 

32 raise ResponseDecodingException("Unable to decode JSON body as UTF-8") from exc 

33 else: 

34 raise ResponseDecodingException("Missing response body for JSON decoding") 

35 if raw_text.strip() == "": 

36 raise ResponseDecodingException("Missing response body for JSON decoding") 

37 

38 try: 

39 json_data: object = json.loads(raw_text) 

40 except json.JSONDecodeError as exc: 

41 raise ResponseDecodingException("Invalid JSON response body") from exc 

42 

43 return BaobabResponse( 

44 status_code=response.status_code, 

45 headers=response.headers, 

46 text=response.text, 

47 content=response.content, 

48 json_data=json_data, 

49 ) 

50 

51 @staticmethod 

52 def _is_json_content_type(content_type: str) -> bool: 

53 media_type = content_type.split(";", 1)[0].strip().lower() 

54 if media_type == "application/json": 

55 return True 

56 return media_type.startswith("application/") and media_type.endswith("+json")