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/core/url_area.py
CHANGED
|
@@ -3,7 +3,6 @@ from dataclasses import dataclass
|
|
|
3
3
|
from textual import on
|
|
4
4
|
from textual.app import ComposeResult
|
|
5
5
|
from textual.message import Message
|
|
6
|
-
from textual.reactive import Reactive
|
|
7
6
|
from textual.widgets import Button, ContentSwitcher, Input, Select, Static
|
|
8
7
|
|
|
9
8
|
from restiny.enums import HTTPMethod
|
|
@@ -29,8 +28,6 @@ class URLArea(Static):
|
|
|
29
28
|
}
|
|
30
29
|
"""
|
|
31
30
|
|
|
32
|
-
request_pending = Reactive(False)
|
|
33
|
-
|
|
34
31
|
class SendRequest(Message):
|
|
35
32
|
"""
|
|
36
33
|
Sent when the user send a request.
|
|
@@ -47,11 +44,15 @@ class URLArea(Static):
|
|
|
47
44
|
def __init__(self) -> None:
|
|
48
45
|
super().__init__()
|
|
49
46
|
|
|
47
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
48
|
+
super().__init__(*args, **kwargs)
|
|
49
|
+
self._request_pending = False
|
|
50
|
+
|
|
50
51
|
def compose(self) -> ComposeResult:
|
|
51
52
|
yield Select.from_values(
|
|
52
53
|
values=HTTPMethod.values(), allow_blank=False, id='method'
|
|
53
54
|
)
|
|
54
|
-
yield Input(placeholder='Enter URL', id='url')
|
|
55
|
+
yield Input(placeholder='Enter URL', select_on_focus=False, id='url')
|
|
55
56
|
with ContentSwitcher(
|
|
56
57
|
id='request-button-switcher', initial='send-request'
|
|
57
58
|
):
|
|
@@ -78,9 +79,28 @@ class URLArea(Static):
|
|
|
78
79
|
self.send_request_button = self.query_one('#send-request', Button)
|
|
79
80
|
self.cancel_request_button = self.query_one('#cancel-request', Button)
|
|
80
81
|
|
|
82
|
+
def get_data(self) -> URLAreaData:
|
|
83
|
+
return URLAreaData(
|
|
84
|
+
method=self.method_select.value,
|
|
85
|
+
url=self.url_input.value,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def request_pending(self) -> bool:
|
|
90
|
+
return self._request_pending
|
|
91
|
+
|
|
92
|
+
@request_pending.setter
|
|
93
|
+
def request_pending(self, value: bool) -> None:
|
|
94
|
+
if value is True:
|
|
95
|
+
self._request_button_switcher.current = 'cancel-request'
|
|
96
|
+
elif value is False:
|
|
97
|
+
self._request_button_switcher.current = 'send-request'
|
|
98
|
+
|
|
99
|
+
self._request_pending = value
|
|
100
|
+
|
|
81
101
|
@on(Button.Pressed, '#send-request')
|
|
82
|
-
@on(Input.Submitted)
|
|
83
|
-
def
|
|
102
|
+
@on(Input.Submitted, '#url')
|
|
103
|
+
def _on_send_request(
|
|
84
104
|
self, message: Button.Pressed | Input.Submitted
|
|
85
105
|
) -> None:
|
|
86
106
|
if self.request_pending:
|
|
@@ -88,22 +108,10 @@ class URLArea(Static):
|
|
|
88
108
|
|
|
89
109
|
self.post_message(message=self.SendRequest())
|
|
90
110
|
|
|
91
|
-
@on(Input.Submitted)
|
|
92
111
|
@on(Button.Pressed, '#cancel-request')
|
|
93
|
-
|
|
112
|
+
@on(Input.Submitted, '#url')
|
|
113
|
+
def _on_cancel_request(self, message: Button.Pressed) -> None:
|
|
94
114
|
if not self.request_pending:
|
|
95
115
|
return
|
|
96
116
|
|
|
97
117
|
self.post_message(message=self.CancelRequest())
|
|
98
|
-
|
|
99
|
-
def watch_request_pending(self, value: bool) -> None:
|
|
100
|
-
if value is True:
|
|
101
|
-
self._request_button_switcher.current = 'cancel-request'
|
|
102
|
-
elif value is False:
|
|
103
|
-
self._request_button_switcher.current = 'send-request'
|
|
104
|
-
|
|
105
|
-
def get_data(self) -> URLAreaData:
|
|
106
|
-
return URLAreaData(
|
|
107
|
-
method=self.method_select.value,
|
|
108
|
-
url=self.url_input.value,
|
|
109
|
-
)
|
restiny/enums.py
CHANGED
|
@@ -41,3 +41,10 @@ class ContentType(StrEnum):
|
|
|
41
41
|
XML = 'application/xml'
|
|
42
42
|
FORM_URLENCODED = 'application/x-www-form-urlencoded'
|
|
43
43
|
FORM_MULTIPART = 'multipart/form-data'
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class AuthMode(StrEnum):
|
|
47
|
+
BASIC = 'basic'
|
|
48
|
+
BEARER = 'bearer'
|
|
49
|
+
API_KEY = 'api_key'
|
|
50
|
+
DIGEST = 'digest'
|
restiny/httpx_auths.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from collections.abc import Generator
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BearerAuth(httpx.Auth):
|
|
7
|
+
"""
|
|
8
|
+
Adds a Bearer token to the Authorization header of each request.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, token: str) -> None:
|
|
12
|
+
self._token = token
|
|
13
|
+
|
|
14
|
+
def auth_flow(
|
|
15
|
+
self, request: httpx.Request
|
|
16
|
+
) -> Generator[httpx.Request, httpx.Response]:
|
|
17
|
+
request.headers['authorization'] = f'Bearer {self._token}'
|
|
18
|
+
yield request
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class APIKeyHeaderAuth(httpx.Auth):
|
|
22
|
+
"""
|
|
23
|
+
Adds an API key to the request headers.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, key: str, value: str) -> None:
|
|
27
|
+
self._key = key
|
|
28
|
+
self._value = value
|
|
29
|
+
|
|
30
|
+
def auth_flow(
|
|
31
|
+
self, request: httpx.Request
|
|
32
|
+
) -> Generator[httpx.Request, httpx.Response]:
|
|
33
|
+
request.headers[self._key] = self._value
|
|
34
|
+
yield request
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class APIKeyParamAuth(httpx.Auth):
|
|
38
|
+
"""
|
|
39
|
+
Adds an API key as a query parameter to the request URL.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self, key: str, value: str) -> None:
|
|
43
|
+
self._key = key
|
|
44
|
+
self._value = value
|
|
45
|
+
|
|
46
|
+
def auth_flow(
|
|
47
|
+
self, request: httpx.Request
|
|
48
|
+
) -> Generator[httpx.Request, httpx.Response]:
|
|
49
|
+
request.url = request.url.copy_with(
|
|
50
|
+
params=request.url.params.set(self._key, self._value)
|
|
51
|
+
)
|
|
52
|
+
yield request
|
restiny/test.py
ADDED
restiny/utils.py
CHANGED
|
@@ -8,32 +8,43 @@ import httpx
|
|
|
8
8
|
def build_curl_cmd(
|
|
9
9
|
method: str,
|
|
10
10
|
url: str,
|
|
11
|
-
headers: dict[str, str] =
|
|
12
|
-
params: dict[str, str] =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
headers: dict[str, str] | None = None,
|
|
12
|
+
params: dict[str, str] | None = None,
|
|
13
|
+
body_raw: str | None = None,
|
|
14
|
+
body_form_urlencoded: dict[str, str] | None = None,
|
|
15
|
+
body_form_multipart: dict[str, str | Path] | None = None,
|
|
16
|
+
body_files: list[Path] | None = None,
|
|
17
|
+
auth_basic: tuple[str, str] | None = None,
|
|
18
|
+
auth_bearer: str | None = None,
|
|
19
|
+
auth_api_key_header: tuple[str, str] | None = None,
|
|
20
|
+
auth_api_key_param: tuple[str, str] | None = None,
|
|
21
|
+
auth_digest: tuple[str, str] | None = None,
|
|
17
22
|
) -> str:
|
|
18
23
|
cmd_parts = ['curl']
|
|
24
|
+
|
|
25
|
+
# Method
|
|
19
26
|
cmd_parts.extend(['--request', method])
|
|
20
27
|
|
|
21
|
-
|
|
28
|
+
# URL + Params
|
|
29
|
+
if params:
|
|
30
|
+
url = str(httpx.URL(url).copy_merge_params(params))
|
|
22
31
|
cmd_parts.extend(['--url', shlex.quote(url)])
|
|
23
32
|
|
|
33
|
+
# Headers
|
|
24
34
|
for header_key, header_value in headers.items():
|
|
25
35
|
header = f'{header_key}: {header_value}'
|
|
26
36
|
cmd_parts.extend(['--header', shlex.quote(header)])
|
|
27
37
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
38
|
+
# Body
|
|
39
|
+
if body_raw:
|
|
40
|
+
cmd_parts.extend(['--data', shlex.quote(body_raw)])
|
|
41
|
+
elif body_form_urlencoded:
|
|
42
|
+
for form_key, form_value in body_form_urlencoded.items():
|
|
32
43
|
cmd_parts.extend(
|
|
33
44
|
['--data', shlex.quote(f'{form_key}={form_value}')]
|
|
34
45
|
)
|
|
35
|
-
elif
|
|
36
|
-
for form_key, form_value in
|
|
46
|
+
elif body_form_multipart:
|
|
47
|
+
for form_key, form_value in body_form_multipart.items():
|
|
37
48
|
if isinstance(form_value, str):
|
|
38
49
|
cmd_parts.extend(
|
|
39
50
|
['--form', shlex.quote(f'{form_key}={form_value}')]
|
|
@@ -42,10 +53,29 @@ def build_curl_cmd(
|
|
|
42
53
|
cmd_parts.extend(
|
|
43
54
|
['--form', shlex.quote(f'{form_key}=@{form_value}')]
|
|
44
55
|
)
|
|
45
|
-
elif
|
|
46
|
-
for file in
|
|
56
|
+
elif body_files:
|
|
57
|
+
for file in body_files:
|
|
47
58
|
cmd_parts.extend(['--data', shlex.quote(f'@{file}')])
|
|
48
59
|
|
|
60
|
+
# Auth
|
|
61
|
+
if auth_basic:
|
|
62
|
+
user, pwd = auth_basic
|
|
63
|
+
cmd_parts.extend(['--user', shlex.quote(f'{user}:{pwd}')])
|
|
64
|
+
elif auth_bearer:
|
|
65
|
+
token = auth_bearer
|
|
66
|
+
cmd_parts.extend(['--header', shlex.quote(f'Authorization: {token}')])
|
|
67
|
+
elif auth_api_key_header:
|
|
68
|
+
key, value = auth_api_key_header
|
|
69
|
+
cmd_parts.extend(['--header', shlex.quote(f'{key}: {value}')])
|
|
70
|
+
elif auth_api_key_param:
|
|
71
|
+
key, value = auth_api_key_param
|
|
72
|
+
url_arg_index = cmd_parts.index('--url')
|
|
73
|
+
new_url = str(httpx.URL(url).copy_merge_params({key: value}))
|
|
74
|
+
cmd_parts[url_arg_index + 1] = shlex.quote(new_url)
|
|
75
|
+
elif auth_digest:
|
|
76
|
+
user, pwd = auth_digest
|
|
77
|
+
cmd_parts.extend(['--digest', '--user', shlex.quote(f'{user}:{pwd}')])
|
|
78
|
+
|
|
49
79
|
return ' '.join(cmd_parts)
|
|
50
80
|
|
|
51
81
|
|
restiny/widgets/__init__.py
CHANGED
|
@@ -5,6 +5,7 @@ This module contains reusable widgets used in the DataFox interface.
|
|
|
5
5
|
from restiny.widgets.custom_directory_tree import CustomDirectoryTree
|
|
6
6
|
from restiny.widgets.custom_text_area import CustomTextArea
|
|
7
7
|
from restiny.widgets.dynamic_fields import DynamicFields, TextDynamicField
|
|
8
|
+
from restiny.widgets.password_input import PasswordInput
|
|
8
9
|
from restiny.widgets.path_chooser import PathChooser
|
|
9
10
|
|
|
10
11
|
__all__ = [
|
|
@@ -13,4 +14,5 @@ __all__ = [
|
|
|
13
14
|
'CustomDirectoryTree',
|
|
14
15
|
'CustomTextArea',
|
|
15
16
|
'PathChooser',
|
|
17
|
+
'PasswordInput',
|
|
16
18
|
]
|