Coverage for src\baobab_web_api_caller\core\request_url_builder.py: 90%
27 statements
« prev ^ index » next coverage.py v7.10.3, created at 2026-03-21 12:10 +0100
« prev ^ index » next coverage.py v7.10.3, created at 2026-03-21 12:10 +0100
1"""Construction d'URL de requête."""
3from __future__ import annotations
5from dataclasses import dataclass
6from urllib.parse import urlencode
8from baobab_web_api_caller.core.baobab_request import BaobabRequest
9from baobab_web_api_caller.exceptions.configuration_exception import ConfigurationException
12@dataclass(frozen=True, slots=True)
13class RequestUrlBuilder:
14 """Construit l'URL finale à partir d'une base URL et d'une requête.
16 :param base_url: Base URL normalisée (sans ``/`` final).
17 :type base_url: str
18 :raises ConfigurationException: Si la base URL est invalide.
19 """
21 base_url: str
23 def __post_init__(self) -> None:
24 if not isinstance(self.base_url, str) or self.base_url.strip() == "": 24 ↛ 25line 24 didn't jump to line 25 because the condition on line 24 was never true
25 raise ConfigurationException("base_url must be a non-empty string")
26 if self.base_url.endswith("/"):
27 raise ConfigurationException("base_url must not end with '/'")
29 def build(self, request: BaobabRequest) -> str:
30 """Construit l'URL à appeler.
32 :param request: Requête.
33 :type request: BaobabRequest
34 :return: URL finale.
35 :rtype: str
36 """
38 if not request.path.startswith("/"): 38 ↛ 39line 38 didn't jump to line 39 because the condition on line 38 was never true
39 raise ConfigurationException("request.path must start with '/'")
41 url = f"{self.base_url}{request.path}"
42 if request.query_params:
43 items: list[tuple[str, str]] = []
44 for key, value in request.query_params.items():
45 if isinstance(value, str):
46 items.append((key, value))
47 else:
48 for v in value:
49 items.append((key, v))
50 query = urlencode(items, doseq=True, safe=":/")
51 return f"{url}?{query}"
52 return url