Terarea  2
The automation project
Loading...
Searching...
No Matches
query_boilerplate.py
Go to the documentation of this file.
1"""_summary_
2 File in charge of containing the boilerplate endpoint functions to make queries to the web.
3"""
4
5from typing import Union, Mapping, Dict, Any
6
7import requests
8
9from display_tty import Disp, TOML_CONF, FILE_DESCRIPTOR, SAVE_TO_FILE, FILE_NAME
10
11from . import constants as ACONST
12
13
14class UnknownContentTypeError(Exception):
15 """Exception raised when the content type of the response is unknown."""
16
17 def __init__(self, message: str = "No Content-Type found in the header") -> None:
18 self.message = message
19 super().__init__(self.message)
20
21
23 """_summary_
24 This is the class in charge of containing the boilerplate endpoint functions.
25 """
26
27 def __init__(self, host: str = "http://127.0.0.1", port: Union[int, None] = 6000, delay: int = 2, debug: bool = False) -> None:
28 """_summary_
29 Class in charge of containing the boilerplate endpoint functions to make queries to the web.
30
31 Args:
32 host (_type_, optional): _description_. Defaults to "http://127.0.0.1".
33 port (Union[int, None], optional): _description_. Defaults to 6000.
34 delay (int, optional): _description_. Defaults to 2.
35 debug (bool, optional): _description_. Defaults to False.
36 """
37 self.debug: bool = debug
38 if host.startswith("http") is False:
39 self._host = f"http://{host}"
40 else:
41 self._host = host
42 if self._host.endswith("/") is True:
43 self._host = self._host[:-1]
44 if port is not None:
45 self._host += f":{port}"
46 self._delay = delay
47 # ---------------------- The visual logger class ----------------------
48 self.disp: Disp = Disp(
49 TOML_CONF,
50 SAVE_TO_FILE,
51 FILE_NAME,
52 FILE_DESCRIPTOR,
53 debug=self.debug,
54 logger=self.__class__.__name__
55 )
56
57 def get_endpoint(self, path: str, content: Union[Dict[str, Any], None] = None, header: Union[Mapping[str, str], None] = None) -> requests.Response:
58 """_summary_
59 This function is in charge of sending a GET request to the server.
60 Args:
61 path (str): _description_: The path of the endpoint.
62 content (Union[Dict[str, Any], None], optional): _description_: The content to be sent to the server.
63 header (Union[Mapping[str, str], None], optional): _description_: The header to be sent to the server. Defaults to None.
64 Returns:
65 requests.Response: _description_: The response from the server.
66 """
67 title = "get_endpoint"
68 if isinstance(path, str) is False:
69 raise ValueError(
70 f"Expected an input of type string but got {type(path)}"
71 )
72 if path[0] == "/":
73 path = path[1:]
74 final_path = f"{self._host}/{path}"
75 self.disp.log_debug(f"final_path = {final_path}", title)
76 self.disp.log_debug(f"content = {content}", title)
77 self.disp.log_debug(f"header = {header}", title)
78 if content is not None and header is None:
79 return requests.get(final_path, json=content, timeout=self._delay)
80 if content is None and header is not None:
81 return requests.get(final_path, headers=header, timeout=self._delay)
82 if content is not None and header is not None:
83 return requests.get(final_path, json=content, headers=header, timeout=self._delay)
84 return requests.get(final_path, timeout=self._delay)
85
86 def post_endpoint(self, path: str, content: Union[Dict[str, Any], None] = None, header: Union[Mapping[str, str], None] = None) -> requests.Response:
87 """_summary_
88 This function is in charge of sending a POST request to the server.
89 Args:
90 path (str): _description_: The path of the endpoint.
91 content (Union[Dict[str, Any], None], optional): _description_: The content to be sent to the server.
92 header (Union[Mapping[str, str], None], optional): _description_: The header to be sent to the server. Defaults to None.
93 Returns:
94 requests.Response: _description_: The response from the server.
95 """
96 title = "post_endpoint"
97 if isinstance(path, str) is False:
98 raise ValueError(
99 f"Expected an input of type string but got {type(path)}"
100 )
101 if path[0] == "/":
102 path = path[1:]
103 final_path = f"{self._host}/{path}"
104 self.disp.log_debug(f"final_path = {final_path}", title)
105 self.disp.log_debug(f"content = {content}", title)
106 self.disp.log_debug(f"header = {header}", title)
107 if content is not None and header is None:
108 return requests.post(final_path, json=content, timeout=self._delay)
109 if content is None and header is not None:
110 return requests.post(final_path, headers=header, timeout=self._delay)
111 if content is not None and header is not None:
112 return requests.post(final_path, json=content, headers=header, timeout=self._delay)
113 return requests.post(final_path, timeout=self._delay)
114
115 def put_endpoint(self, path: str, content: Union[Dict[str, Any], None] = None, header: Union[Mapping[str, str], None] = None) -> requests.Response:
116 """_summary_
117 This function is in charge of sending a PUT request to the server.
118 Args:
119 path (str): _description_: The path of the endpoint.
120 content (Union[Dict[str, Any], None], optional): _description_: The content to be sent to the server.
121 header (Union[Mapping[str, str], None], optional): _description_: The header to be sent to the server. Defaults to None.
122 Returns:
123 requests.Response: _description_: The response from the server.
124 """
125 title = "put_endpoint"
126 if isinstance(path, str) is False:
127 raise ValueError(
128 f"Expected an input of type string but got {type(path)}"
129 )
130 if path[0] == "/":
131 path = path[1:]
132 final_path = f"{self._host}/{path}"
133 self.disp.log_debug(f"final_path = {final_path}", title)
134 self.disp.log_debug(f"content = {content}", title)
135 self.disp.log_debug(f"header = {header}", title)
136 if content is not None and header is None:
137 return requests.put(final_path, json=content, timeout=self._delay)
138 if content is None and header is not None:
139 return requests.put(final_path, headers=header, timeout=self._delay)
140 if content is not None and header is not None:
141 return requests.put(final_path, json=content, headers=header, timeout=self._delay)
142 return requests.put(final_path, timeout=self._delay)
143
144 def patch_endpoint(self, path: str, content: Union[Dict[str, Any], None] = None, header: Union[Mapping[str, str], None] = None) -> requests.Response:
145 """_summary_
146 This function is in charge of sending a PATCH request to the server.
147 Args:
148 path (str): _description_: The path of the endpoint.
149 content (Union[Dict[str, Any], None], optional): _description_: The content to be sent to the server.
150 header (Union[Mapping[str, str], None], optional): _description_: The header to be sent to the server. Defaults to None.
151 Returns:
152 requests.Response: _description_: The response from the server.
153 """
154 title = "patch_endpoint"
155 if isinstance(path, str) is False:
156 raise ValueError(
157 f"Expected an input of type string but got {type(path)}"
158 )
159 if path[0] == "/":
160 path = path[1:]
161 final_path = f"{self._host}/{path}"
162 self.disp.log_debug(f"final_path = {final_path}", title)
163 self.disp.log_debug(f"content = {content}", title)
164 self.disp.log_debug(f"header = {header}", title)
165 if content is not None and header is None:
166 return requests.patch(final_path, json=content, timeout=self._delay)
167 if content is None and header is not None:
168 return requests.patch(final_path, headers=header, timeout=self._delay)
169 if content is not None and header is not None:
170 return requests.patch(final_path, json=content, headers=header, timeout=self._delay)
171 return requests.patch(final_path, timeout=self._delay)
172
173 def delete_endpoint(self, path: str, content: Union[Dict[str, Any], None] = None, header: Union[Mapping[str, str], None] = None) -> requests.Response:
174 """_summary_
175 This function is in charge of sending a DELETE request to the server.
176 Args:
177 path (str): _description_: The path of the endpoint.
178 content (Union[Dict[str, Any], None], optional): _description_: The content to be sent to the server.
179 header (Union[Mapping[str, str], None], optional): _description_: The header to be sent to the server. Defaults to None.
180 Returns:
181 requests.Response: _description_: The response from the server.
182 """
183 title = "delete_endpoint"
184 if isinstance(path, str) is False:
185 raise ValueError(
186 f"Expected an input of type string but got {type(path)}"
187 )
188 if path[0] == "/":
189 path = path[1:]
190 final_path = f"{self._host}/{path}"
191 self.disp.log_debug(f"final_path = {final_path}", title)
192 self.disp.log_debug(f"content = {content}", title)
193 self.disp.log_debug(f"header = {header}", title)
194 if content is not None and header is None:
195 return requests.delete(final_path, json=content, timeout=self._delay)
196 if content is None and header is not None:
197 return requests.delete(final_path, headers=header, timeout=self._delay)
198 if content is not None and header is not None:
199 return requests.delete(final_path, json=content, headers=header, timeout=self._delay)
200 return requests.delete(final_path, timeout=self._delay)
201
202 def head_endpoint(self, path: str, content: Union[Dict[str, Any], None] = None, header: Union[Mapping[str, str], None] = None) -> requests.Response:
203 """_summary_
204 This function is in charge of sending a HEAD request to the server.
205 Args:
206 path (str): _description_: The path of the endpoint.
207 content (Union[Dict[str, Any], None], optional): _description_: The content to be sent to the server.
208 header (Union[Mapping[str, str], None], optional): _description_: The header to be sent to the server. Defaults to None.
209 Returns:
210 requests.Response: _description_: The response from the server.
211 """
212 title = "head_endpoint"
213 if isinstance(path, str) is False:
214 raise ValueError(
215 f"Expected an input of type string but got {type(path)}"
216 )
217 if path[0] == "/":
218 path = path[1:]
219 final_path = f"{self._host}/{path}"
220 self.disp.log_debug(f"final_path = {final_path}", title)
221 self.disp.log_debug(f"content = {content}", title)
222 self.disp.log_debug(f"header = {header}", title)
223 if content is not None and header is None:
224 return requests.head(final_path, json=content, timeout=self._delay)
225 if content is None and header is not None:
226 return requests.head(final_path, headers=header, timeout=self._delay)
227 if content is not None and header is not None:
228 return requests.head(final_path, json=content, headers=header, timeout=self._delay)
229 return requests.head(final_path, timeout=self._delay)
230
231 def options_endpoint(self, path: str, content: Union[Dict[str, Any], None] = None, header: Union[Mapping[str, str], None] = None) -> requests.Response:
232 """_summary_
233 This function is in charge of sending a OPTIONS request to the server.
234 Args:
235 path (str): _description_: The path of the endpoint.
236 content (Union[Dict[str, Any], None], optional): _description_: The content to be sent to the server.
237 header (Union[Mapping[str, str], None], optional): _description_: The header to be sent to the server. Defaults to None.
238 Returns:
239 requests.Response: _description_: The response from the server.
240 """
241 title = "options_endpoint"
242 if isinstance(path, str) is False:
243 raise ValueError(
244 f"Expected an input of type string but got {type(path)}"
245 )
246 if path[0] == "/":
247 path = path[1:]
248 final_path = f"{self._host}/{path}"
249 self.disp.log_debug(f"final_path = {final_path}", title)
250 self.disp.log_debug(f"content = {content}", title)
251 self.disp.log_debug(f"header = {header}", title)
252 if content is not None and header is None:
253 return requests.options(final_path, json=content, timeout=self._delay)
254 if content is None and header is not None:
255 return requests.options(final_path, headers=header, timeout=self._delay)
256 if content is not None and header is not None:
257 return requests.options(final_path, json=content, headers=header, timeout=self._delay)
258 return requests.options(final_path, timeout=self._delay)
259
260 def get_status(self, response: requests.Response) -> int:
261 """_summary_
262 This function is in charge of getting the status code from the response.
263 Args:
264 response (requests.Response): _description_: The response from the server.
265 Returns:
266 int: _description_: The status code from the response.
267 """
268 return response.status_code
269
270 def get_content_type(self, response: requests.Response) -> str:
271 """_summary_
272 This function is in charge of getting the content type from the response.
273
274 Args:
275 response (requests.Response): _description_: The response from the server.
276
277 Raises:
278 UnknownContentTypeError: _description_: If no content type is found in the header.
279
280 Returns:
281 str: _description_: The content type from the response.
282 """
283 response = response.headers.get("Content-Type")
284 if response is not None:
285 return response
286 raise UnknownContentTypeError("No Content-Type found in the header")
287
288 def get_content(self, response: requests.Response) -> Dict[str, Union[str, bytes, Dict[str, Any], None]]:
289 """
290 Retrieve and parse content from an HTTP response based on its Content-Type.
291
292 Args:
293 response (requests.Response): The HTTP response from the server.
294
295 Raises:
296 ValueError: If the response content is not valid JSON.
297
298 Returns:
299 Dict[str, Union[str, bytes, Dict[str, Any], None]]: A dictionary with parsed response content.
300 Contains two keys:
301 - ACONST.CONTENT_TYPE_KEY: str - The content type of the response.
302 - ACONST.CONTENT_KEY: Union[Dict[str, Any], str, bytes, None] - Parsed content.
303 - JSON (application/json, application/ld+json) -> Dict
304 - Text (text/html, text/plain, text/csv, text/xml) -> str
305 - XML (application/xml) -> str
306 - Binary data (e.g., application/octet-stream, application/pdf) -> bytes
307 - None for unhandled types.
308 """
309 title = "get_content"
310 node = None
311 try:
312 content_type = self.get_content_type(response)
313 node = content_type.split(";")[0]
314 except UnknownContentTypeError as e:
315 self.disp.log_error(
316 f"Response content type is unknown, {e}", title
317 )
318 return {ACONST.CONTENT_TYPE_KEY: None, ACONST.CONTENT_KEY: None}
319
320 if node in ACONST.CONTENT_TYPES_JSON:
321 try:
322 return {ACONST.CONTENT_TYPE_KEY: content_type, ACONST.CONTENT_KEY: response.json()}
323 except ValueError as e:
324 raise ValueError("Response content is not valid JSON") from e
325 elif node in ACONST.CONTENT_TYPES_TEXT:
326 return {ACONST.CONTENT_TYPE_KEY: content_type, ACONST.CONTENT_KEY: response.text}
327 elif node in ACONST.CONTENT_TYPES_XML:
328 return {ACONST.CONTENT_TYPE_KEY: content_type, ACONST.CONTENT_KEY: response.text}
329 elif node in ACONST.CONTENT_TYPES_BINARY:
330 return {ACONST.CONTENT_TYPE_KEY: content_type, ACONST.CONTENT_KEY: response.content}
331 elif node in ACONST.CONTENT_TYPES_AUDIO:
332 return {ACONST.CONTENT_TYPE_KEY: content_type, ACONST.CONTENT_KEY: response.content}
333 elif node in ACONST.CONTENT_TYPES_IMAGES:
334 return {ACONST.CONTENT_TYPE_KEY: content_type, ACONST.CONTENT_KEY: response.content}
335 elif node in ACONST.CONTENT_TYPES_VIDEO:
336 return {ACONST.CONTENT_TYPE_KEY: content_type, ACONST.CONTENT_KEY: response.content}
337 else:
338 self.disp.log_error(f"Unhandled content type: {content_type}")
339 return {ACONST.CONTENT_TYPE_KEY: content_type, ACONST.CONTENT_KEY: None}
340
341 def compile_response_data(self, response: requests.Response) -> Dict[str, Union[int, Dict[str, Union[str, bytes, Dict[str, Any], None]]]]:
342 """
343 Compile the response from an HTTP request into a dictionary.
344
345 Args:
346 response (requests.Response): The HTTP response from the server.
347
348 Returns:
349 Dict[str, Union[int, Dict[str, Union[str, bytes, Dict[str, Any], None]]]: A dictionary with the response status code and content.
350 """
351 title = "compile_response_data"
352 self.disp.log_debug("Compiling response data", title)
353 self.disp.log_debug(f"response = {response}", title)
354 compiled = {}
355 data = self.get_content(response)
356 self.disp.log_debug(f"data = {data}", title)
357 compiled[ACONST.RESPONSE_NODE_BODY_KEY] = data[ACONST.CONTENT_KEY]
358 compiled[ACONST.RESPONSE_NODE_BODY_TYPE_KEY] = data[ACONST.CONTENT_TYPE_KEY]
359 compiled[ACONST.RESPONSE_NODE_STATUS_CODE_KEY] = response.status_code
360 compiled[ACONST.RESPONSE_NODE_HEADERS_KEY] = response.headers
361 compiled[ACONST.RESPONSE_NODE_HEADERS_TYPE_KEY] = type(
362 response.headers)
363 compiled[ACONST.RESPONSE_NODE_ENCODING_KEY] = response.encoding
364 compiled[ACONST.RESPONSE_NODE_HISTORY_KEY] = response.history
365 compiled[ACONST.RESPONSE_NODE_COOKIES_KEY] = response.cookies
366 compiled[ACONST.RESPONSE_NODE_ELAPSED_KEY] = response.elapsed
367 compiled[ACONST.RESPONSE_NODE_REASON_KEY] = response.reason
368 compiled[ACONST.RESPONSE_NODE_URL_KEY] = response.url
369 compiled[ACONST.RESPONSE_NODE_URL_KEY] = response.request.method
370 self.disp.log_debug(f"compiled = {compiled}", title)
371 return compiled
Dict[str, Union[str, bytes, Dict[str, Any], None]] get_content(self, requests.Response response)
requests.Response put_endpoint(self, str path, Union[Dict[str, Any], None] content=None, Union[Mapping[str, str], None] header=None)
requests.Response delete_endpoint(self, str path, Union[Dict[str, Any], None] content=None, Union[Mapping[str, str], None] header=None)
int get_status(self, requests.Response response)
requests.Response post_endpoint(self, str path, Union[Dict[str, Any], None] content=None, Union[Mapping[str, str], None] header=None)
Dict[str, Union[int, Dict[str, Union[str, bytes, Dict[str, Any], None]]]] compile_response_data(self, requests.Response response)
None __init__(self, str host="http://127.0.0.1", Union[int, None] port=6000, int delay=2, bool debug=False)
requests.Response head_endpoint(self, str path, Union[Dict[str, Any], None] content=None, Union[Mapping[str, str], None] header=None)
str get_content_type(self, requests.Response response)
requests.Response patch_endpoint(self, str path, Union[Dict[str, Any], None] content=None, Union[Mapping[str, str], None] header=None)
requests.Response get_endpoint(self, str path, Union[Dict[str, Any], None] content=None, Union[Mapping[str, str], None] header=None)
requests.Response options_endpoint(self, str path, Union[Dict[str, Any], None] content=None, Union[Mapping[str, str], None] header=None)
None __init__(self, str message="No Content-Type found in the header")