restiny 0.2.1__py3-none-any.whl → 0.6.1__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.
- restiny/__about__.py +1 -1
- restiny/__main__.py +28 -14
- restiny/assets/style.tcss +56 -2
- restiny/consts.py +236 -0
- restiny/data/db.py +60 -0
- restiny/data/models.py +111 -0
- restiny/data/repos.py +455 -0
- restiny/data/sql/__init__.py +3 -0
- restiny/entities.py +438 -0
- restiny/enums.py +14 -5
- restiny/httpx_auths.py +52 -0
- restiny/ui/__init__.py +17 -0
- restiny/ui/app.py +586 -0
- restiny/ui/collections_area.py +594 -0
- restiny/ui/environments_screen.py +270 -0
- restiny/ui/request_area.py +602 -0
- restiny/{core → ui}/response_area.py +4 -1
- restiny/ui/settings_screen.py +73 -0
- restiny/ui/top_bar_area.py +60 -0
- restiny/{core → ui}/url_area.py +54 -38
- restiny/utils.py +52 -15
- restiny/widgets/__init__.py +15 -1
- restiny/widgets/collections_tree.py +74 -0
- restiny/widgets/confirm_prompt.py +76 -0
- restiny/widgets/custom_input.py +20 -0
- restiny/widgets/dynamic_fields.py +65 -70
- restiny/widgets/password_input.py +161 -0
- restiny/widgets/path_chooser.py +12 -12
- {restiny-0.2.1.dist-info → restiny-0.6.1.dist-info}/METADATA +7 -5
- restiny-0.6.1.dist-info/RECORD +38 -0
- restiny/core/__init__.py +0 -15
- restiny/core/app.py +0 -348
- restiny/core/request_area.py +0 -337
- restiny-0.2.1.dist-info/RECORD +0 -24
- {restiny-0.2.1.dist-info → restiny-0.6.1.dist-info}/WHEEL +0 -0
- {restiny-0.2.1.dist-info → restiny-0.6.1.dist-info}/entry_points.txt +0 -0
- {restiny-0.2.1.dist-info → restiny-0.6.1.dist-info}/licenses/LICENSE +0 -0
- {restiny-0.2.1.dist-info → restiny-0.6.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
from textual import on
|
|
4
|
+
from textual.app import ComposeResult
|
|
5
|
+
from textual.binding import Binding
|
|
6
|
+
from textual.containers import Horizontal, Vertical
|
|
7
|
+
from textual.screen import ModalScreen
|
|
8
|
+
from textual.widgets import Button, Label, ListItem, ListView, Rule
|
|
9
|
+
|
|
10
|
+
from restiny.entities import Environment
|
|
11
|
+
from restiny.widgets import DynamicFields, TextDynamicField
|
|
12
|
+
from restiny.widgets.custom_input import CustomInput
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from restiny.ui.app import RESTinyApp
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class EnvironmentsScreen(ModalScreen):
|
|
19
|
+
app: 'RESTinyApp'
|
|
20
|
+
|
|
21
|
+
DEFAULT_CSS = """
|
|
22
|
+
EnvironmentsScreen {
|
|
23
|
+
align: center middle;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#modal-content {
|
|
27
|
+
width: 70%;
|
|
28
|
+
height: 80%;
|
|
29
|
+
border: heavy black;
|
|
30
|
+
border-title-color: gray;
|
|
31
|
+
background: $surface;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
Label {
|
|
35
|
+
padding-left: 4;
|
|
36
|
+
}
|
|
37
|
+
"""
|
|
38
|
+
AUTO_FOCUS = '#environments-list'
|
|
39
|
+
|
|
40
|
+
BINDINGS = [
|
|
41
|
+
Binding(
|
|
42
|
+
key='escape',
|
|
43
|
+
action='dismiss',
|
|
44
|
+
description='Quit the screen',
|
|
45
|
+
show=False,
|
|
46
|
+
),
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
def __init__(self) -> None:
|
|
50
|
+
super().__init__()
|
|
51
|
+
|
|
52
|
+
def compose(self) -> ComposeResult:
|
|
53
|
+
with Vertical(id='modal-content'):
|
|
54
|
+
with Horizontal(classes='w-auto p-1'):
|
|
55
|
+
with Vertical(classes='w-1fr h-auto'):
|
|
56
|
+
yield ListView(
|
|
57
|
+
classes='',
|
|
58
|
+
id='environments-list',
|
|
59
|
+
)
|
|
60
|
+
with Horizontal(classes='w-auto h-auto mt-1'):
|
|
61
|
+
yield CustomInput(
|
|
62
|
+
placeholder='Environment name',
|
|
63
|
+
classes='w-4fr',
|
|
64
|
+
id='environment-name',
|
|
65
|
+
)
|
|
66
|
+
yield Button(
|
|
67
|
+
label='➕',
|
|
68
|
+
tooltip='Add environment',
|
|
69
|
+
classes='',
|
|
70
|
+
id='add-environment',
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
yield Rule(orientation='vertical')
|
|
74
|
+
|
|
75
|
+
with Vertical(classes='w-2fr h-auto'):
|
|
76
|
+
with Horizontal(classes='w-auto h-auto'):
|
|
77
|
+
yield CustomInput(
|
|
78
|
+
placeholder='Environment name',
|
|
79
|
+
classes='w-5fr',
|
|
80
|
+
id='environment-rename',
|
|
81
|
+
)
|
|
82
|
+
yield Button(
|
|
83
|
+
'💾',
|
|
84
|
+
tooltip='Save environment',
|
|
85
|
+
classes='w-1fr',
|
|
86
|
+
id='save-environment',
|
|
87
|
+
)
|
|
88
|
+
yield Button(
|
|
89
|
+
'➖',
|
|
90
|
+
tooltip='Delete environment',
|
|
91
|
+
classes='w-1fr',
|
|
92
|
+
id='delete-environment',
|
|
93
|
+
)
|
|
94
|
+
yield DynamicFields(
|
|
95
|
+
fields=[
|
|
96
|
+
TextDynamicField(enabled=False, key='', value='')
|
|
97
|
+
],
|
|
98
|
+
classes='mt-1',
|
|
99
|
+
id='variables',
|
|
100
|
+
)
|
|
101
|
+
yield Label(
|
|
102
|
+
"[i]Tip: You can use [b]'{{var}}'[/] or [b]'${var}'[/] in requests to reference variables.[/]",
|
|
103
|
+
classes='mt-1',
|
|
104
|
+
)
|
|
105
|
+
with Horizontal(classes='w-auto h-auto'):
|
|
106
|
+
yield Button(label='Close', classes='w-1fr', id='close')
|
|
107
|
+
|
|
108
|
+
async def on_mount(self) -> None:
|
|
109
|
+
self.modal_content = self.query_one('#modal-content', Vertical)
|
|
110
|
+
self.environments_list = self.query_one('#environments-list', ListView)
|
|
111
|
+
self.environment_name_input = self.query_one(
|
|
112
|
+
'#environment-name', CustomInput
|
|
113
|
+
)
|
|
114
|
+
self.add_environment_button = self.query_one(
|
|
115
|
+
'#add-environment', Button
|
|
116
|
+
)
|
|
117
|
+
self.environment_rename_input = self.query_one(
|
|
118
|
+
'#environment-rename', CustomInput
|
|
119
|
+
)
|
|
120
|
+
self.save_environment_button = self.query_one(
|
|
121
|
+
'#save-environment', Button
|
|
122
|
+
)
|
|
123
|
+
self.delete_environment_button = self.query_one(
|
|
124
|
+
'#delete-environment', Button
|
|
125
|
+
)
|
|
126
|
+
self.variables_dynamic_fields = self.query_one(
|
|
127
|
+
'#variables', DynamicFields
|
|
128
|
+
)
|
|
129
|
+
self.close_button = self.query_one('#close', Button)
|
|
130
|
+
|
|
131
|
+
self.modal_content.border_title = 'Environments'
|
|
132
|
+
|
|
133
|
+
await self._populate_environments()
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def _selected_env_id(self) -> int | None:
|
|
137
|
+
if self.environments_list.index is None:
|
|
138
|
+
return None
|
|
139
|
+
|
|
140
|
+
return int(
|
|
141
|
+
self.environments_list.children[
|
|
142
|
+
self.environments_list.index
|
|
143
|
+
].id.rsplit('-', 1)[1]
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def _selected_env_name(self) -> str | None:
|
|
148
|
+
if self.environments_list.index is None:
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
return (
|
|
152
|
+
self.environments_list.children[self.environments_list.index]
|
|
153
|
+
.children[0]
|
|
154
|
+
.content
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
@on(ListView.Selected, '#environments-list')
|
|
158
|
+
async def _on_select_environment(self) -> None:
|
|
159
|
+
self.environment_rename_input.value = self._selected_env_name
|
|
160
|
+
is_global = self._selected_env_name == 'global'
|
|
161
|
+
self.environment_rename_input.disabled = is_global
|
|
162
|
+
self.delete_environment_button.disabled = is_global
|
|
163
|
+
|
|
164
|
+
for field in self.variables_dynamic_fields.fields:
|
|
165
|
+
self.variables_dynamic_fields.remove_field(field=field)
|
|
166
|
+
|
|
167
|
+
environment = self.app.environments_repo.get_by_id(
|
|
168
|
+
self._selected_env_id
|
|
169
|
+
).data
|
|
170
|
+
for variable in environment.variables:
|
|
171
|
+
await self.variables_dynamic_fields.add_field(
|
|
172
|
+
field=TextDynamicField(
|
|
173
|
+
enabled=variable.enabled,
|
|
174
|
+
key=variable.key,
|
|
175
|
+
value=variable.value,
|
|
176
|
+
),
|
|
177
|
+
before_last=True,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
@on(Button.Pressed, '#add-environment')
|
|
181
|
+
@on(CustomInput.Submitted, '#environment-name')
|
|
182
|
+
async def _on_add_environment(self) -> None:
|
|
183
|
+
if not self.environment_name_input.value.strip():
|
|
184
|
+
self.notify('Environment name is required', severity='error')
|
|
185
|
+
return
|
|
186
|
+
|
|
187
|
+
create_resp = self.app.environments_repo.create(
|
|
188
|
+
Environment(name=self.environment_name_input.value, variables=[])
|
|
189
|
+
)
|
|
190
|
+
if not create_resp.ok:
|
|
191
|
+
self.notify(
|
|
192
|
+
f'Failed to create environment ({create_resp.status})',
|
|
193
|
+
severity='error',
|
|
194
|
+
)
|
|
195
|
+
return
|
|
196
|
+
|
|
197
|
+
await self._add_environment(
|
|
198
|
+
name=create_resp.data.name, id=create_resp.data.id
|
|
199
|
+
)
|
|
200
|
+
self.environment_name_input.value = ''
|
|
201
|
+
self.environments_list.index = len(self.environments_list.children) - 1
|
|
202
|
+
await self._on_select_environment()
|
|
203
|
+
self.notify('Environment added', severity='information')
|
|
204
|
+
|
|
205
|
+
@on(Button.Pressed, '#save-environment')
|
|
206
|
+
@on(CustomInput.Submitted, '#environment-rename')
|
|
207
|
+
async def _on_save_environment(self) -> None:
|
|
208
|
+
if not self.environment_rename_input.value.strip():
|
|
209
|
+
self.notify('Environment name is required', severity='error')
|
|
210
|
+
return
|
|
211
|
+
|
|
212
|
+
update_resp = self.app.environments_repo.update(
|
|
213
|
+
Environment(
|
|
214
|
+
id=self._selected_env_id,
|
|
215
|
+
name=self.environment_rename_input.value,
|
|
216
|
+
variables=[
|
|
217
|
+
Environment.Variable(
|
|
218
|
+
enabled=variable.enabled,
|
|
219
|
+
key=variable.key,
|
|
220
|
+
value=variable.value,
|
|
221
|
+
)
|
|
222
|
+
for variable in self.variables_dynamic_fields.filled_fields
|
|
223
|
+
],
|
|
224
|
+
)
|
|
225
|
+
)
|
|
226
|
+
if not update_resp.ok:
|
|
227
|
+
self.notify(
|
|
228
|
+
f'Failed to update environment ({update_resp.status})',
|
|
229
|
+
severity='error',
|
|
230
|
+
)
|
|
231
|
+
return
|
|
232
|
+
|
|
233
|
+
self.environments_list.children[self.environments_list.index].children[
|
|
234
|
+
0
|
|
235
|
+
].update(update_resp.data.name)
|
|
236
|
+
self.notify('Environment updated', severity='information')
|
|
237
|
+
|
|
238
|
+
@on(Button.Pressed, '#delete-environment')
|
|
239
|
+
async def _on_remove_environment(self) -> None:
|
|
240
|
+
if self.environments_list.index is None:
|
|
241
|
+
self.notify('No environment selected', severity='error')
|
|
242
|
+
return
|
|
243
|
+
|
|
244
|
+
self.app.environments_repo.delete_by_id(self._selected_env_id)
|
|
245
|
+
focus_target_index = max(0, self.environments_list.index - 1)
|
|
246
|
+
await self.environments_list.children[
|
|
247
|
+
self.environments_list.index
|
|
248
|
+
].remove()
|
|
249
|
+
self.environments_list.index = focus_target_index
|
|
250
|
+
await self._on_select_environment()
|
|
251
|
+
self.notify('Environment removed', severity='information')
|
|
252
|
+
|
|
253
|
+
@on(Button.Pressed, '#close')
|
|
254
|
+
async def _on_close(self, message: Button.Pressed) -> None:
|
|
255
|
+
self.dismiss(result=None)
|
|
256
|
+
|
|
257
|
+
async def _populate_environments(self) -> None:
|
|
258
|
+
environments = self.app.environments_repo.list().data
|
|
259
|
+
for environment in environments:
|
|
260
|
+
await self._add_environment(
|
|
261
|
+
name=environment.name, id=environment.id
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
self.environments_list.index = 0
|
|
265
|
+
await self._on_select_environment()
|
|
266
|
+
|
|
267
|
+
async def _add_environment(self, name: str, id: int) -> None:
|
|
268
|
+
await self.environments_list.mount(
|
|
269
|
+
ListItem(Label(name), id=f'env-{id}')
|
|
270
|
+
)
|