Coverage for src\zapy\requests\converter.py: 99%
93 statements
« prev ^ index » next coverage.py v7.3.4, created at 2023-12-20 14:17 -0500
« prev ^ index » next coverage.py v7.3.4, created at 2023-12-20 14:17 -0500
1import sys
2import itertools
3from threading import Lock
5from collections import defaultdict
7from zapy.base import ZapyAuto
8from zapy.templating.templating import evaluate, render
9from zapy.templating.eval import sync_exec
11from .exceptions import error_location
12from .context import ZapyRequestContext, build_context_module
13from .hooks import RequestHook
14from .models import KeyValueItem, ZapyRequest, HttpxArguments
15from .file_loader import ZapyFileInfo
18FORM_TYPES = [
19 'application/x-www-form-urlencoded',
20 'multipart/form-data',
21]
23class RequestConverter:
25 def __init__(self, zapy_request: ZapyRequest, ctx: ZapyRequestContext):
26 self.zapy_request = zapy_request
27 self.ctx = ctx
29 # evaluate script
30 request_hooks, variables = self._load_script()
31 self.request_hooks = request_hooks
32 self.variables = variables
34 def build_httpx_args(self) -> HttpxArguments:
35 zapy_request: ZapyRequest = self.zapy_request
37 # variable_declaration
38 self.variables |= self._convert_variables(self.zapy_request.variables)
40 httpx_args = HttpxArguments(
41 method=zapy_request.method,
42 url=self._convert_url(zapy_request.endpoint),
43 params=self._convert_params(zapy_request.params),
44 headers=self._convert_headers(zapy_request.headers,
45 body_content_type=zapy_request.body_type),
46 **self._build_httpx_args_body(zapy_request.body_type, zapy_request.body),
47 )
49 return httpx_args
51 @error_location('body')
52 def _build_httpx_args_body(self, body_type: str, body):
53 files, data, content = None, None, None
54 if body is None or body_type == 'None':
55 data = None
56 elif body_type in FORM_TYPES:
57 data, files = self._convert_body_data(body)
58 else:
59 _body_source = self.__join_code(body)
60 content = self.__render(_body_source)
61 return {
62 'files': files,
63 'data': data,
64 'content': content,
65 }
67 @error_location('body')
68 def _convert_body_data(self, data_list: list[KeyValueItem]):
69 data_list: list[KeyValueItem] = filter(lambda x: x.active and x.key.strip(), data_list)
70 result_dict = defaultdict(list)
71 files = list()
72 for param in data_list:
73 value = self.__eval_var(param.value)
74 if isinstance(value, ZapyFileInfo):
75 file_info = (
76 value.file_name,
77 open(value.file_location, mode="rb"),
78 *([] if value.mime_type is ZapyAuto else [value.mime_type])
79 )
80 files.append((param.key, file_info))
81 else:
82 result_dict[param.key].append(str(value))
84 return dict(result_dict), files
86 @error_location('url')
87 def _convert_url(self, endpoint) -> dict:
88 return self.__render(endpoint)
90 @error_location('params')
91 def _convert_params(self, parameter_list: list[KeyValueItem]) -> dict:
92 active_params = filter(lambda x: x.active and x.key.strip(), parameter_list)
93 groups = itertools.groupby(active_params, lambda x: x.key.strip())
94 result_dict = {
95 key: [self.__render(p.value) for p in params]
96 for key, params in groups
97 }
99 return result_dict
101 @error_location('headers')
102 def _convert_headers(self, header_list: list[KeyValueItem], body_content_type=None) -> dict:
103 headers = dict()
104 for x in header_list:
105 key = x.key.strip()
106 if not (x.active and key): 106 ↛ 107line 106 didn't jump to line 107, because the condition on line 106 was never true
107 continue
108 eval_var = self.__eval_var(x.value)
109 if key.lower() == 'content-type' and eval_var == ZapyAuto:
110 if body_content_type not in ('None', 'multipart/form-data'):
111 headers[key] = str(body_content_type)
112 else:
113 headers[key] = str(eval_var)
115 return headers
117 @error_location('variables')
118 def _convert_variables(self, variable_list: list[KeyValueItem]):
119 return {
120 x.key.strip(): self.__eval_var(x.value)
121 for x in variable_list if x.active and x.key.strip()
122 }
124 @error_location('script')
125 def _load_script(self) -> tuple[RequestHook, dict]:
126 script = self.__join_code(self.zapy_request.script)
127 self.script = script
129 module_context = build_context_module(self.ctx)
130 vars = {
131 'print' : self.ctx.logger,
132 'ctx' : module_context,
133 }
135 if script is None or not script.strip():
136 return RequestHook(), vars
138 with Lock():
139 sys.modules['zapy.ctx'] = module_context
140 sync_exec(script, vars)
141 request_hook = module_context.hooks.request_hook
143 return request_hook, vars
145 def __eval_var(self, value):
146 return evaluate(value, self.variables)
148 def __render(self, source):
149 return render(source, self.variables)
151 def __join_code(self, code):
152 if type(code) == str:
153 return code
154 else:
155 return "\n".join(code)