restiny 0.2.0__py3-none-any.whl → 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of restiny might be problematic. Click here for more details.
- restiny/__about__.py +1 -1
- restiny/assets/style.tcss +13 -1
- restiny/core/app.py +288 -207
- restiny/core/request_area.py +281 -112
- restiny/core/response_area.py +53 -20
- restiny/core/url_area.py +28 -20
- restiny/enums.py +7 -0
- restiny/httpx_auths.py +52 -0
- restiny/test.py +13 -0
- restiny/utils.py +45 -15
- restiny/widgets/__init__.py +2 -0
- restiny/widgets/dynamic_fields.py +129 -103
- restiny/widgets/password_input.py +159 -0
- restiny/widgets/path_chooser.py +49 -28
- {restiny-0.2.0.dist-info → restiny-0.3.0.dist-info}/METADATA +2 -2
- restiny-0.3.0.dist-info/RECORD +27 -0
- restiny/screens/__init__.py +0 -0
- restiny/screens/dialog.py +0 -109
- restiny-0.2.0.dist-info/RECORD +0 -26
- {restiny-0.2.0.dist-info → restiny-0.3.0.dist-info}/WHEEL +0 -0
- {restiny-0.2.0.dist-info → restiny-0.3.0.dist-info}/entry_points.txt +0 -0
- {restiny-0.2.0.dist-info → restiny-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {restiny-0.2.0.dist-info → restiny-0.3.0.dist-info}/top_level.txt +0 -0
restiny/__about__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '0.
|
|
1
|
+
__version__ = '0.3.0'
|
restiny/assets/style.tcss
CHANGED
|
@@ -3,6 +3,10 @@ Button {
|
|
|
3
3
|
max-width: 100%;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
Input {
|
|
7
|
+
width: 1fr;
|
|
8
|
+
}
|
|
9
|
+
|
|
6
10
|
.hidden {
|
|
7
11
|
display: none;
|
|
8
12
|
}
|
|
@@ -19,6 +23,10 @@ Button {
|
|
|
19
23
|
width: 2fr;
|
|
20
24
|
}
|
|
21
25
|
|
|
26
|
+
.w-3fr {
|
|
27
|
+
width: 3fr;
|
|
28
|
+
}
|
|
29
|
+
|
|
22
30
|
.h-auto {
|
|
23
31
|
height: auto;
|
|
24
32
|
}
|
|
@@ -28,7 +36,11 @@ Button {
|
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
.h-2fr {
|
|
31
|
-
|
|
39
|
+
height: 2fr;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.h-3fr {
|
|
43
|
+
height: 3fr;
|
|
32
44
|
}
|
|
33
45
|
|
|
34
46
|
.p-0 {
|
restiny/core/app.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import json
|
|
3
3
|
import mimetypes
|
|
4
|
+
from http import HTTPStatus
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
|
|
6
7
|
import httpx
|
|
@@ -13,9 +14,23 @@ from textual.events import DescendantFocus
|
|
|
13
14
|
from textual.widget import Widget
|
|
14
15
|
from textual.widgets import Footer, Header
|
|
15
16
|
|
|
17
|
+
from restiny import httpx_auths
|
|
16
18
|
from restiny.__about__ import __version__
|
|
17
19
|
from restiny.assets import STYLE_TCSS
|
|
18
|
-
from restiny.core import
|
|
20
|
+
from restiny.core import (
|
|
21
|
+
RequestArea,
|
|
22
|
+
RequestAreaData,
|
|
23
|
+
ResponseArea,
|
|
24
|
+
URLArea,
|
|
25
|
+
URLAreaData,
|
|
26
|
+
)
|
|
27
|
+
from restiny.core.request_area import (
|
|
28
|
+
APIKeyAuth,
|
|
29
|
+
BasicAuth,
|
|
30
|
+
BearerAuth,
|
|
31
|
+
DigestAuth,
|
|
32
|
+
)
|
|
33
|
+
from restiny.core.response_area import ResponseAreaData
|
|
19
34
|
from restiny.enums import BodyMode, BodyRawLanguage, ContentType
|
|
20
35
|
from restiny.utils import build_curl_cmd
|
|
21
36
|
|
|
@@ -56,10 +71,8 @@ class RESTinyApp(App, inherit_bindings=False):
|
|
|
56
71
|
with Horizontal(classes='h-auto'):
|
|
57
72
|
yield URLArea()
|
|
58
73
|
with Horizontal(classes='h-1fr'):
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
with Vertical():
|
|
62
|
-
yield ResponseArea()
|
|
74
|
+
yield RequestArea()
|
|
75
|
+
yield ResponseArea()
|
|
63
76
|
yield Footer()
|
|
64
77
|
|
|
65
78
|
def on_mount(self) -> None:
|
|
@@ -67,25 +80,6 @@ class RESTinyApp(App, inherit_bindings=False):
|
|
|
67
80
|
self.request_area = self.query_one(RequestArea)
|
|
68
81
|
self.response_area = self.query_one(ResponseArea)
|
|
69
82
|
|
|
70
|
-
@on(DescendantFocus)
|
|
71
|
-
def on_focus(self, event: DescendantFocus) -> None:
|
|
72
|
-
self.last_focused_widget = event.widget
|
|
73
|
-
last_focused_maximizable_area = self.find_maximizable_area_by_widget(
|
|
74
|
-
widget=event.widget
|
|
75
|
-
)
|
|
76
|
-
if last_focused_maximizable_area:
|
|
77
|
-
self.last_focused_maximizable_area = last_focused_maximizable_area
|
|
78
|
-
|
|
79
|
-
@on(URLArea.SendRequest)
|
|
80
|
-
def on_send_request(self, message: URLArea.SendRequest) -> None:
|
|
81
|
-
self.response_area.reset_response()
|
|
82
|
-
self.current_request = asyncio.create_task(self.send_request())
|
|
83
|
-
|
|
84
|
-
@on(URLArea.CancelRequest)
|
|
85
|
-
def on_cancel_request(self, message: URLArea.CancelRequest) -> None:
|
|
86
|
-
if self.current_request and not self.current_request.done():
|
|
87
|
-
self.current_request.cancel()
|
|
88
|
-
|
|
89
83
|
def action_maximize_or_minimize_area(self) -> None:
|
|
90
84
|
if self.screen.maximized:
|
|
91
85
|
self.screen.minimize()
|
|
@@ -107,76 +101,128 @@ class RESTinyApp(App, inherit_bindings=False):
|
|
|
107
101
|
headers[header.key] = header.value
|
|
108
102
|
|
|
109
103
|
params = {}
|
|
110
|
-
for param in request_area_data.
|
|
104
|
+
for param in request_area_data.params:
|
|
111
105
|
if not param.enabled:
|
|
112
106
|
continue
|
|
113
107
|
|
|
114
108
|
params[param.key] = param.value
|
|
115
109
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if request_area_data.body.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
110
|
+
body_raw = None
|
|
111
|
+
body_form_urlencoded = {}
|
|
112
|
+
body_form_multipart = {}
|
|
113
|
+
body_files = None
|
|
114
|
+
if request_area_data.body.enabled:
|
|
115
|
+
if request_area_data.body.mode == BodyMode.RAW:
|
|
116
|
+
body_raw = request_area_data.body.payload
|
|
117
|
+
elif request_area_data.body.mode == BodyMode.FORM_URLENCODED:
|
|
118
|
+
body_form_urlencoded = {
|
|
119
|
+
form_field.key: form_field.value
|
|
120
|
+
for form_field in request_area_data.body.payload
|
|
121
|
+
if form_field.enabled
|
|
122
|
+
}
|
|
123
|
+
elif request_area_data.body.mode == BodyMode.FORM_MULTIPART:
|
|
124
|
+
body_form_multipart = {
|
|
125
|
+
form_field.key: form_field.value
|
|
126
|
+
for form_field in request_area_data.body.payload
|
|
127
|
+
if form_field.enabled
|
|
128
|
+
}
|
|
129
|
+
elif request_area_data.body.mode == BodyMode.FILE:
|
|
130
|
+
body_files = [request_area_data.body.payload]
|
|
131
|
+
|
|
132
|
+
auth_basic = None
|
|
133
|
+
auth_bearer = None
|
|
134
|
+
auth_api_key_header = None
|
|
135
|
+
auth_api_key_param = None
|
|
136
|
+
auth_digest = None
|
|
137
|
+
if request_area_data.auth.enabled:
|
|
138
|
+
if isinstance(request_area_data.auth.value, BasicAuth):
|
|
139
|
+
auth_basic = (
|
|
140
|
+
request_area_data.auth.value.username,
|
|
141
|
+
request_area_data.auth.value.password,
|
|
142
|
+
)
|
|
143
|
+
elif isinstance(request_area_data.auth.value, BearerAuth):
|
|
144
|
+
auth_bearer = request_area_data.auth.value.token
|
|
145
|
+
elif isinstance(request_area_data.auth.value, APIKeyAuth):
|
|
146
|
+
if request_area_data.auth.value.where == 'header':
|
|
147
|
+
auth_api_key_header = (
|
|
148
|
+
request_area_data.auth.value.key,
|
|
149
|
+
request_area_data.auth.value.value,
|
|
150
|
+
)
|
|
151
|
+
elif request_area_data.auth.value.where == 'param':
|
|
152
|
+
auth_api_key_param = (
|
|
153
|
+
request_area_data.auth.value.key,
|
|
154
|
+
request_area_data.auth.value.value,
|
|
155
|
+
)
|
|
156
|
+
elif isinstance(request_area_data.auth.value, DigestAuth):
|
|
157
|
+
auth_digest = (
|
|
158
|
+
request_area_data.auth.value.username,
|
|
159
|
+
request_area_data.auth.value.password,
|
|
160
|
+
)
|
|
136
161
|
|
|
137
162
|
curl_cmd = build_curl_cmd(
|
|
138
163
|
method=method,
|
|
139
164
|
url=url,
|
|
140
165
|
headers=headers,
|
|
141
166
|
params=params,
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
167
|
+
body_raw=body_raw,
|
|
168
|
+
body_form_urlencoded=body_form_urlencoded,
|
|
169
|
+
body_form_multipart=body_form_multipart,
|
|
170
|
+
body_files=body_files,
|
|
171
|
+
auth_basic=auth_basic,
|
|
172
|
+
auth_bearer=auth_bearer,
|
|
173
|
+
auth_api_key_header=auth_api_key_header,
|
|
174
|
+
auth_api_key_param=auth_api_key_param,
|
|
175
|
+
auth_digest=auth_digest,
|
|
146
176
|
)
|
|
147
|
-
self.
|
|
148
|
-
pyperclip.copy(curl_cmd)
|
|
177
|
+
self.copy_to_clipboard(curl_cmd)
|
|
149
178
|
self.notify(
|
|
150
|
-
|
|
179
|
+
'Command CURL copied to clipboard',
|
|
151
180
|
severity='information',
|
|
152
181
|
)
|
|
153
182
|
|
|
154
|
-
def
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
183
|
+
def copy_to_clipboard(self, text: str) -> None:
|
|
184
|
+
super().copy_to_clipboard(text)
|
|
185
|
+
try:
|
|
186
|
+
# Also copy to the system clipboard (outside of the app)
|
|
187
|
+
pyperclip.copy(text)
|
|
188
|
+
except Exception:
|
|
189
|
+
pass
|
|
190
|
+
|
|
191
|
+
@on(DescendantFocus)
|
|
192
|
+
def _on_focus(self, event: DescendantFocus) -> None:
|
|
193
|
+
self.last_focused_widget = event.widget
|
|
194
|
+
last_focused_maximizable_area = self._find_maximizable_area_by_widget(
|
|
195
|
+
widget=event.widget
|
|
196
|
+
)
|
|
197
|
+
if last_focused_maximizable_area:
|
|
198
|
+
self.last_focused_maximizable_area = last_focused_maximizable_area
|
|
199
|
+
|
|
200
|
+
@on(URLArea.SendRequest)
|
|
201
|
+
def _on_send_request(self, message: URLArea.SendRequest) -> None:
|
|
202
|
+
self.current_request = asyncio.create_task(self._send_request())
|
|
203
|
+
|
|
204
|
+
@on(URLArea.CancelRequest)
|
|
205
|
+
def _on_cancel_request(self, message: URLArea.CancelRequest) -> None:
|
|
206
|
+
if self.current_request and not self.current_request.done():
|
|
207
|
+
self.current_request.cancel()
|
|
208
|
+
|
|
209
|
+
def _find_maximizable_area_by_widget(
|
|
210
|
+
self, widget: Widget
|
|
211
|
+
) -> Widget | None:
|
|
160
212
|
while widget is not None:
|
|
161
|
-
if
|
|
213
|
+
if (
|
|
214
|
+
isinstance(widget, URLArea)
|
|
215
|
+
or isinstance(widget, RequestArea)
|
|
216
|
+
or isinstance(widget, ResponseArea)
|
|
217
|
+
):
|
|
162
218
|
return widget
|
|
163
219
|
widget = widget.parent
|
|
164
220
|
|
|
165
|
-
async def
|
|
221
|
+
async def _send_request(self) -> None:
|
|
166
222
|
url_area_data = self.url_area.get_data()
|
|
167
223
|
request_area_data = self.request_area.get_data()
|
|
168
224
|
|
|
169
|
-
|
|
170
|
-
header.key: header.value
|
|
171
|
-
for header in request_area_data.headers
|
|
172
|
-
if header.enabled
|
|
173
|
-
}
|
|
174
|
-
query_params: dict[str, str] = {
|
|
175
|
-
param.key: param.value
|
|
176
|
-
for param in request_area_data.query_params
|
|
177
|
-
if param.enabled
|
|
178
|
-
}
|
|
179
|
-
|
|
225
|
+
self.response_area.set_data(data=None)
|
|
180
226
|
self.response_area.loading = True
|
|
181
227
|
self.url_area.request_pending = True
|
|
182
228
|
try:
|
|
@@ -185,115 +231,17 @@ class RESTinyApp(App, inherit_bindings=False):
|
|
|
185
231
|
follow_redirects=request_area_data.options.follow_redirects,
|
|
186
232
|
verify=request_area_data.options.verify_ssl,
|
|
187
233
|
) as http_client:
|
|
188
|
-
request =
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
raw = request_area_data.body.payload
|
|
200
|
-
|
|
201
|
-
if (
|
|
202
|
-
request_area_data.body.raw_language
|
|
203
|
-
== BodyRawLanguage.JSON
|
|
204
|
-
):
|
|
205
|
-
headers['content-type'] = ContentType.JSON
|
|
206
|
-
try:
|
|
207
|
-
raw = json.dumps(raw)
|
|
208
|
-
except Exception:
|
|
209
|
-
pass
|
|
210
|
-
elif (
|
|
211
|
-
request_area_data.body.raw_language
|
|
212
|
-
== BodyRawLanguage.YAML
|
|
213
|
-
):
|
|
214
|
-
headers['content-type'] = ContentType.YAML
|
|
215
|
-
elif (
|
|
216
|
-
request_area_data.body.raw_language
|
|
217
|
-
== BodyRawLanguage.HTML
|
|
218
|
-
):
|
|
219
|
-
headers['content-type'] = ContentType.HTML
|
|
220
|
-
elif (
|
|
221
|
-
request_area_data.body.raw_language
|
|
222
|
-
== BodyRawLanguage.XML
|
|
223
|
-
):
|
|
224
|
-
headers['content-type'] = ContentType.XML
|
|
225
|
-
elif (
|
|
226
|
-
request_area_data.body.raw_language
|
|
227
|
-
== BodyRawLanguage.PLAIN
|
|
228
|
-
):
|
|
229
|
-
headers['content-type'] = ContentType.TEXT
|
|
230
|
-
|
|
231
|
-
request = http_client.build_request(
|
|
232
|
-
method=url_area_data.method,
|
|
233
|
-
url=url_area_data.url,
|
|
234
|
-
headers=headers,
|
|
235
|
-
params=query_params,
|
|
236
|
-
content=raw,
|
|
237
|
-
)
|
|
238
|
-
elif request_area_data.body.type == BodyMode.FILE:
|
|
239
|
-
file = request_area_data.body.payload
|
|
240
|
-
if 'content-type' not in headers:
|
|
241
|
-
headers['content-type'] = (
|
|
242
|
-
mimetypes.guess_type(file.name)[0]
|
|
243
|
-
or 'application/octet-stream'
|
|
244
|
-
)
|
|
245
|
-
request = http_client.build_request(
|
|
246
|
-
method=url_area_data.method,
|
|
247
|
-
url=url_area_data.url,
|
|
248
|
-
headers=headers,
|
|
249
|
-
params=query_params,
|
|
250
|
-
content=file.read_bytes(),
|
|
251
|
-
)
|
|
252
|
-
elif (
|
|
253
|
-
request_area_data.body.type == BodyMode.FORM_URLENCODED
|
|
254
|
-
):
|
|
255
|
-
form_urlencoded = {
|
|
256
|
-
form_item.key: form_item.value
|
|
257
|
-
for form_item in request_area_data.body.payload
|
|
258
|
-
if form_item.enabled
|
|
259
|
-
}
|
|
260
|
-
request = http_client.build_request(
|
|
261
|
-
method=url_area_data.method,
|
|
262
|
-
url=url_area_data.url,
|
|
263
|
-
headers=headers,
|
|
264
|
-
params=query_params,
|
|
265
|
-
data=form_urlencoded,
|
|
266
|
-
)
|
|
267
|
-
elif (
|
|
268
|
-
request_area_data.body.type == BodyMode.FORM_MULTIPART
|
|
269
|
-
):
|
|
270
|
-
form_multipart_str = {
|
|
271
|
-
form_item.key: form_item.value
|
|
272
|
-
for form_item in request_area_data.body.payload
|
|
273
|
-
if form_item.enabled
|
|
274
|
-
and isinstance(form_item.value, str)
|
|
275
|
-
}
|
|
276
|
-
form_multipart_files = {
|
|
277
|
-
form_item.key: (
|
|
278
|
-
form_item.value.name,
|
|
279
|
-
form_item.value.read_bytes(),
|
|
280
|
-
mimetypes.guess_type(form_item.value.name)[0]
|
|
281
|
-
or 'application/octet-stream',
|
|
282
|
-
)
|
|
283
|
-
for form_item in request_area_data.body.payload
|
|
284
|
-
if form_item.enabled
|
|
285
|
-
and isinstance(form_item.value, Path)
|
|
286
|
-
}
|
|
287
|
-
request = http_client.build_request(
|
|
288
|
-
method=url_area_data.method,
|
|
289
|
-
url=url_area_data.url,
|
|
290
|
-
headers=headers,
|
|
291
|
-
params=query_params,
|
|
292
|
-
data=form_multipart_str,
|
|
293
|
-
files=form_multipart_files,
|
|
294
|
-
)
|
|
295
|
-
|
|
296
|
-
response = await http_client.send(request=request)
|
|
234
|
+
request = self._build_request(
|
|
235
|
+
http_client=http_client,
|
|
236
|
+
url_area_data=url_area_data,
|
|
237
|
+
request_area_data=request_area_data,
|
|
238
|
+
)
|
|
239
|
+
auth = self._build_auth(
|
|
240
|
+
request_area_data=request_area_data,
|
|
241
|
+
)
|
|
242
|
+
response = await http_client.send(request=request, auth=auth)
|
|
243
|
+
self._display_response(response=response)
|
|
244
|
+
self.response_area.is_showing_response = True
|
|
297
245
|
except httpx.RequestError as error:
|
|
298
246
|
error_name = type(error).__name__
|
|
299
247
|
error_message = str(error)
|
|
@@ -301,42 +249,175 @@ class RESTinyApp(App, inherit_bindings=False):
|
|
|
301
249
|
self.notify(f'{error_name}: {error_message}', severity='error')
|
|
302
250
|
else:
|
|
303
251
|
self.notify(f'{error_name}', severity='error')
|
|
304
|
-
self.response_area.
|
|
252
|
+
self.response_area.set_data(data=None)
|
|
253
|
+
self.response_area.is_showing_response = False
|
|
305
254
|
except asyncio.CancelledError:
|
|
306
|
-
self.response_area.
|
|
307
|
-
|
|
308
|
-
self.display_response(response=response)
|
|
309
|
-
self.response_area.has_response = True
|
|
255
|
+
self.response_area.set_data(data=None)
|
|
256
|
+
self.response_area.is_showing_response = False
|
|
310
257
|
finally:
|
|
311
258
|
self.response_area.loading = False
|
|
312
259
|
self.url_area.request_pending = False
|
|
313
260
|
|
|
314
|
-
def
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
261
|
+
def _build_request(
|
|
262
|
+
self,
|
|
263
|
+
http_client: httpx.AsyncClient,
|
|
264
|
+
url_area_data: URLAreaData,
|
|
265
|
+
request_area_data: RequestAreaData,
|
|
266
|
+
) -> tuple[httpx.Request, httpx.Auth | None]:
|
|
267
|
+
headers: dict[str, str] = {
|
|
268
|
+
header.key: header.value
|
|
269
|
+
for header in request_area_data.headers
|
|
270
|
+
if header.enabled
|
|
271
|
+
}
|
|
272
|
+
params: dict[str, str] = {
|
|
273
|
+
param.key: param.value
|
|
274
|
+
for param in request_area_data.params
|
|
275
|
+
if param.enabled
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if not request_area_data.body.enabled:
|
|
279
|
+
return http_client.build_request(
|
|
280
|
+
method=url_area_data.method,
|
|
281
|
+
url=url_area_data.url,
|
|
282
|
+
headers=headers,
|
|
283
|
+
params=params,
|
|
318
284
|
)
|
|
319
285
|
|
|
320
|
-
|
|
321
|
-
|
|
286
|
+
if request_area_data.body.mode == BodyMode.RAW:
|
|
287
|
+
raw_language_to_content_type = {
|
|
288
|
+
BodyRawLanguage.JSON: ContentType.JSON,
|
|
289
|
+
BodyRawLanguage.YAML: ContentType.YAML,
|
|
290
|
+
BodyRawLanguage.HTML: ContentType.HTML,
|
|
291
|
+
BodyRawLanguage.XML: ContentType.XML,
|
|
292
|
+
BodyRawLanguage.PLAIN: ContentType.TEXT,
|
|
293
|
+
}
|
|
294
|
+
headers['content-type'] = raw_language_to_content_type.get(
|
|
295
|
+
request_area_data.body.raw_language, ContentType.TEXT
|
|
296
|
+
)
|
|
322
297
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
298
|
+
raw = request_area_data.body.payload
|
|
299
|
+
if headers['content-type'] == ContentType.JSON:
|
|
300
|
+
try:
|
|
301
|
+
raw = json.dumps(raw)
|
|
302
|
+
except Exception:
|
|
303
|
+
pass
|
|
304
|
+
|
|
305
|
+
return http_client.build_request(
|
|
306
|
+
method=url_area_data.method,
|
|
307
|
+
url=url_area_data.url,
|
|
308
|
+
headers=headers,
|
|
309
|
+
params=params,
|
|
310
|
+
content=raw,
|
|
311
|
+
)
|
|
312
|
+
elif request_area_data.body.mode == BodyMode.FILE:
|
|
313
|
+
file = request_area_data.body.payload
|
|
314
|
+
if 'content-type' not in headers:
|
|
315
|
+
headers['content-type'] = (
|
|
316
|
+
mimetypes.guess_type(file.name)[0]
|
|
317
|
+
or 'application/octet-stream'
|
|
318
|
+
)
|
|
319
|
+
return http_client.build_request(
|
|
320
|
+
method=url_area_data.method,
|
|
321
|
+
url=url_area_data.url,
|
|
322
|
+
headers=headers,
|
|
323
|
+
params=params,
|
|
324
|
+
content=file.read_bytes(),
|
|
325
|
+
)
|
|
326
|
+
elif request_area_data.body.mode == BodyMode.FORM_URLENCODED:
|
|
327
|
+
form_urlencoded = {
|
|
328
|
+
form_item.key: form_item.value
|
|
329
|
+
for form_item in request_area_data.body.payload
|
|
330
|
+
if form_item.enabled
|
|
331
|
+
}
|
|
332
|
+
return http_client.build_request(
|
|
333
|
+
method=url_area_data.method,
|
|
334
|
+
url=url_area_data.url,
|
|
335
|
+
headers=headers,
|
|
336
|
+
params=params,
|
|
337
|
+
data=form_urlencoded,
|
|
338
|
+
)
|
|
339
|
+
elif request_area_data.body.mode == BodyMode.FORM_MULTIPART:
|
|
340
|
+
form_multipart_str = {
|
|
341
|
+
form_item.key: form_item.value
|
|
342
|
+
for form_item in request_area_data.body.payload
|
|
343
|
+
if form_item.enabled and isinstance(form_item.value, str)
|
|
344
|
+
}
|
|
345
|
+
form_multipart_files = {
|
|
346
|
+
form_item.key: (
|
|
347
|
+
form_item.value.name,
|
|
348
|
+
form_item.value.read_bytes(),
|
|
349
|
+
mimetypes.guess_type(form_item.value.name)[0]
|
|
350
|
+
or 'application/octet-stream',
|
|
327
351
|
)
|
|
352
|
+
for form_item in request_area_data.body.payload
|
|
353
|
+
if form_item.enabled and isinstance(form_item.value, Path)
|
|
354
|
+
}
|
|
355
|
+
return http_client.build_request(
|
|
356
|
+
method=url_area_data.method,
|
|
357
|
+
url=url_area_data.url,
|
|
358
|
+
headers=headers,
|
|
359
|
+
params=params,
|
|
360
|
+
data=form_multipart_str,
|
|
361
|
+
files=form_multipart_files,
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
def _build_auth(
|
|
365
|
+
self,
|
|
366
|
+
request_area_data: RequestAreaData,
|
|
367
|
+
) -> httpx.Auth | None:
|
|
368
|
+
auth = request_area_data.auth
|
|
328
369
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
370
|
+
if not auth.enabled:
|
|
371
|
+
return
|
|
372
|
+
|
|
373
|
+
if isinstance(auth.value, BasicAuth):
|
|
374
|
+
return httpx.BasicAuth(
|
|
375
|
+
username=auth.value.username,
|
|
376
|
+
password=auth.value.password,
|
|
377
|
+
)
|
|
378
|
+
elif isinstance(auth.value, BearerAuth):
|
|
379
|
+
return httpx_auths.BearerAuth(token=auth.value.token)
|
|
380
|
+
elif isinstance(auth.value, APIKeyAuth):
|
|
381
|
+
if auth.value.where == 'header':
|
|
382
|
+
return httpx_auths.APIKeyHeaderAuth(
|
|
383
|
+
key=auth.value.key, value=auth.value.value
|
|
384
|
+
)
|
|
385
|
+
elif auth.value.where == 'param':
|
|
386
|
+
return httpx_auths.APIKeyParamAuth(
|
|
387
|
+
key=auth.value.key, value=auth.value.value
|
|
388
|
+
)
|
|
389
|
+
elif isinstance(auth.value, DigestAuth):
|
|
390
|
+
return httpx.DigestAuth(
|
|
391
|
+
username=auth.value.username,
|
|
392
|
+
password=auth.value.password,
|
|
337
393
|
)
|
|
338
394
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
395
|
+
def _display_response(self, response: httpx.Response) -> None:
|
|
396
|
+
status = HTTPStatus(response.status_code)
|
|
397
|
+
size = response.num_bytes_downloaded
|
|
398
|
+
elapsed_time = round(response.elapsed.total_seconds(), 2)
|
|
399
|
+
headers = {
|
|
400
|
+
header_key: header_value
|
|
401
|
+
for header_key, header_value in response.headers.multi_items()
|
|
402
|
+
}
|
|
403
|
+
content_type_to_body_language = {
|
|
404
|
+
ContentType.TEXT: BodyRawLanguage.PLAIN,
|
|
405
|
+
ContentType.HTML: BodyRawLanguage.HTML,
|
|
406
|
+
ContentType.JSON: BodyRawLanguage.JSON,
|
|
407
|
+
ContentType.YAML: BodyRawLanguage.YAML,
|
|
408
|
+
ContentType.XML: BodyRawLanguage.XML,
|
|
409
|
+
}
|
|
410
|
+
body_raw_language = content_type_to_body_language.get(
|
|
411
|
+
response.headers.get('Content-Type'), BodyRawLanguage.PLAIN
|
|
412
|
+
)
|
|
413
|
+
body_raw = response.text
|
|
414
|
+
self.response_area.set_data(
|
|
415
|
+
data=ResponseAreaData(
|
|
416
|
+
status=status,
|
|
417
|
+
size=size,
|
|
418
|
+
elapsed_time=elapsed_time,
|
|
419
|
+
headers=headers,
|
|
420
|
+
body_raw_language=body_raw_language,
|
|
421
|
+
body_raw=body_raw,
|
|
422
|
+
)
|
|
423
|
+
)
|