restiny 0.5.0__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 +2 -0
- restiny/assets/style.tcss +9 -1
- restiny/data/models.py +36 -15
- restiny/data/repos.py +107 -3
- restiny/entities.py +119 -1
- restiny/ui/__init__.py +2 -0
- restiny/ui/app.py +127 -41
- restiny/ui/collections_area.py +35 -10
- restiny/ui/environments_screen.py +270 -0
- restiny/ui/request_area.py +28 -1
- restiny/ui/response_area.py +4 -1
- restiny/ui/settings_screen.py +6 -13
- restiny/ui/top_bar_area.py +60 -0
- restiny/ui/url_area.py +4 -0
- restiny/widgets/collections_tree.py +5 -1
- {restiny-0.5.0.dist-info → restiny-0.6.1.dist-info}/METADATA +1 -1
- restiny-0.6.1.dist-info/RECORD +38 -0
- restiny-0.5.0.dist-info/RECORD +0 -36
- {restiny-0.5.0.dist-info → restiny-0.6.1.dist-info}/WHEEL +0 -0
- {restiny-0.5.0.dist-info → restiny-0.6.1.dist-info}/entry_points.txt +0 -0
- {restiny-0.5.0.dist-info → restiny-0.6.1.dist-info}/licenses/LICENSE +0 -0
- {restiny-0.5.0.dist-info → restiny-0.6.1.dist-info}/top_level.txt +0 -0
restiny/__about__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '0.
|
|
1
|
+
__version__ = '0.6.1'
|
restiny/__main__.py
CHANGED
|
@@ -15,6 +15,7 @@ def run_app() -> None:
|
|
|
15
15
|
from restiny.consts import CONF_DIR, DB_FILE
|
|
16
16
|
from restiny.data.db import DBManager
|
|
17
17
|
from restiny.data.repos import (
|
|
18
|
+
EnvironmentsSQLRepo,
|
|
18
19
|
FoldersSQLRepo,
|
|
19
20
|
RequestsSQLRepo,
|
|
20
21
|
SettingsSQLRepo,
|
|
@@ -29,6 +30,7 @@ def run_app() -> None:
|
|
|
29
30
|
folders_repo=FoldersSQLRepo(db_manager=db_manager),
|
|
30
31
|
requests_repo=RequestsSQLRepo(db_manager=db_manager),
|
|
31
32
|
settings_repo=SettingsSQLRepo(db_manager=db_manager),
|
|
33
|
+
environments_repo=EnvironmentsSQLRepo(db_manager=db_manager),
|
|
32
34
|
).run()
|
|
33
35
|
|
|
34
36
|
|
restiny/assets/style.tcss
CHANGED
|
@@ -71,6 +71,10 @@ Input {
|
|
|
71
71
|
padding: 0;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
.p-1 {
|
|
75
|
+
padding: 1;
|
|
76
|
+
}
|
|
77
|
+
|
|
74
78
|
.pt-1 {
|
|
75
79
|
padding-top: 1;
|
|
76
80
|
}
|
|
@@ -92,10 +96,14 @@ Input {
|
|
|
92
96
|
padding-right: 1;
|
|
93
97
|
}
|
|
94
98
|
|
|
95
|
-
.m {
|
|
99
|
+
.m-0 {
|
|
96
100
|
margin: 0;
|
|
97
101
|
}
|
|
98
102
|
|
|
103
|
+
.m-1 {
|
|
104
|
+
margin: 1;
|
|
105
|
+
}
|
|
106
|
+
|
|
99
107
|
.mt-1 {
|
|
100
108
|
margin-top: 1;
|
|
101
109
|
}
|
restiny/data/models.py
CHANGED
|
@@ -12,7 +12,7 @@ class SQLFolder(SQLModelBase):
|
|
|
12
12
|
__tablename__ = 'folders'
|
|
13
13
|
|
|
14
14
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
15
|
-
name: Mapped[str] = mapped_column()
|
|
15
|
+
name: Mapped[str] = mapped_column(nullable=False)
|
|
16
16
|
parent_id: Mapped[int | None] = mapped_column(
|
|
17
17
|
ForeignKey('folders.id'), nullable=True
|
|
18
18
|
)
|
|
@@ -38,24 +38,24 @@ class SQLRequest(SQLModelBase):
|
|
|
38
38
|
folder_id: Mapped[int] = mapped_column(
|
|
39
39
|
ForeignKey('folders.id'), nullable=False
|
|
40
40
|
)
|
|
41
|
-
name: Mapped[str] = mapped_column()
|
|
41
|
+
name: Mapped[str] = mapped_column(nullable=False)
|
|
42
42
|
|
|
43
|
-
method: Mapped[str] = mapped_column()
|
|
44
|
-
url: Mapped[str | None] = mapped_column()
|
|
45
|
-
headers: Mapped[str | None] = mapped_column()
|
|
46
|
-
params: Mapped[str | None] = mapped_column()
|
|
43
|
+
method: Mapped[str] = mapped_column(nullable=False)
|
|
44
|
+
url: Mapped[str | None] = mapped_column(nullable=True)
|
|
45
|
+
headers: Mapped[str | None] = mapped_column(nullable=True)
|
|
46
|
+
params: Mapped[str | None] = mapped_column(nullable=True)
|
|
47
47
|
|
|
48
|
-
body_enabled: Mapped[bool] = mapped_column()
|
|
49
|
-
body_mode: Mapped[str] = mapped_column()
|
|
50
|
-
body: Mapped[str | None] = mapped_column()
|
|
48
|
+
body_enabled: Mapped[bool] = mapped_column(nullable=False)
|
|
49
|
+
body_mode: Mapped[str] = mapped_column(nullable=False)
|
|
50
|
+
body: Mapped[str | None] = mapped_column(nullable=True)
|
|
51
51
|
|
|
52
|
-
auth_enabled: Mapped[bool] = mapped_column()
|
|
53
|
-
auth_mode: Mapped[str] = mapped_column()
|
|
54
|
-
auth: Mapped[str | None] = mapped_column()
|
|
52
|
+
auth_enabled: Mapped[bool] = mapped_column(nullable=False)
|
|
53
|
+
auth_mode: Mapped[str] = mapped_column(nullable=False)
|
|
54
|
+
auth: Mapped[str | None] = mapped_column(nullable=True)
|
|
55
55
|
|
|
56
|
-
option_timeout: Mapped[float | None] = mapped_column()
|
|
57
|
-
option_follow_redirects: Mapped[bool] = mapped_column()
|
|
58
|
-
option_verify_ssl: Mapped[bool] = mapped_column()
|
|
56
|
+
option_timeout: Mapped[float | None] = mapped_column(nullable=True)
|
|
57
|
+
option_follow_redirects: Mapped[bool] = mapped_column(nullable=False)
|
|
58
|
+
option_verify_ssl: Mapped[bool] = mapped_column(nullable=False)
|
|
59
59
|
|
|
60
60
|
created_at: Mapped[datetime] = mapped_column(
|
|
61
61
|
DateTime(),
|
|
@@ -88,3 +88,24 @@ class SQLSettings(SQLModelBase):
|
|
|
88
88
|
onupdate=func.current_timestamp(),
|
|
89
89
|
nullable=False,
|
|
90
90
|
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class SQLEnvironment(SQLModelBase):
|
|
94
|
+
__tablename__ = 'environments'
|
|
95
|
+
|
|
96
|
+
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
97
|
+
|
|
98
|
+
name: Mapped[str] = mapped_column(nullable=False, unique=True)
|
|
99
|
+
variables: Mapped[str] = mapped_column(nullable=False)
|
|
100
|
+
|
|
101
|
+
created_at: Mapped[datetime] = mapped_column(
|
|
102
|
+
DateTime(),
|
|
103
|
+
default=func.current_timestamp(),
|
|
104
|
+
nullable=False,
|
|
105
|
+
)
|
|
106
|
+
updated_at: Mapped[datetime] = mapped_column(
|
|
107
|
+
DateTime(),
|
|
108
|
+
default=func.current_timestamp(),
|
|
109
|
+
onupdate=func.current_timestamp(),
|
|
110
|
+
nullable=False,
|
|
111
|
+
)
|
restiny/data/repos.py
CHANGED
|
@@ -8,12 +8,17 @@ from enum import StrEnum
|
|
|
8
8
|
from functools import wraps
|
|
9
9
|
from typing import Any
|
|
10
10
|
|
|
11
|
-
from sqlalchemy import select
|
|
11
|
+
from sqlalchemy import case, select
|
|
12
12
|
from sqlalchemy.exc import IntegrityError, InterfaceError, OperationalError
|
|
13
13
|
|
|
14
14
|
from restiny.data.db import DBManager
|
|
15
|
-
from restiny.data.models import
|
|
16
|
-
|
|
15
|
+
from restiny.data.models import (
|
|
16
|
+
SQLEnvironment,
|
|
17
|
+
SQLFolder,
|
|
18
|
+
SQLRequest,
|
|
19
|
+
SQLSettings,
|
|
20
|
+
)
|
|
21
|
+
from restiny.entities import Environment, Folder, Request, Settings
|
|
17
22
|
|
|
18
23
|
|
|
19
24
|
def safe_repo(func):
|
|
@@ -349,3 +354,102 @@ class SettingsSQLRepo(SQLRepoBase):
|
|
|
349
354
|
created_at=settings.created_at,
|
|
350
355
|
updated_at=settings.updated_at,
|
|
351
356
|
)
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
class EnvironmentsSQLRepo(SQLRepoBase):
|
|
360
|
+
@safe_repo
|
|
361
|
+
def get_by_id(self, id: int) -> RepoResp:
|
|
362
|
+
with self.db_manager.session_scope() as session:
|
|
363
|
+
sql_environment = session.get(SQLEnvironment, id)
|
|
364
|
+
|
|
365
|
+
if not sql_environment:
|
|
366
|
+
return RepoResp(status=RepoStatus.NOT_FOUND)
|
|
367
|
+
|
|
368
|
+
environment = self._sql_to_environment(sql_environment)
|
|
369
|
+
return RepoResp(data=environment)
|
|
370
|
+
|
|
371
|
+
@safe_repo
|
|
372
|
+
def get_by_name(self, name: str) -> RepoResp:
|
|
373
|
+
with self.db_manager.session_scope() as session:
|
|
374
|
+
sql_environment = session.scalar(
|
|
375
|
+
select(SQLEnvironment).where(SQLEnvironment.name == name)
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
if not sql_environment:
|
|
379
|
+
return RepoResp(status=RepoStatus.NOT_FOUND)
|
|
380
|
+
|
|
381
|
+
environment = self._sql_to_environment(sql_environment)
|
|
382
|
+
return RepoResp(data=environment)
|
|
383
|
+
|
|
384
|
+
@safe_repo
|
|
385
|
+
def list(self) -> RepoResp:
|
|
386
|
+
with self.db_manager.session_scope() as session:
|
|
387
|
+
sql_envs = session.scalars(
|
|
388
|
+
select(SQLEnvironment).order_by(
|
|
389
|
+
case((SQLEnvironment.name == 'global', 0), else_=1),
|
|
390
|
+
SQLEnvironment.name.asc(),
|
|
391
|
+
)
|
|
392
|
+
)
|
|
393
|
+
envs = [self._sql_to_environment(sql_env) for sql_env in sql_envs]
|
|
394
|
+
return RepoResp(data=envs)
|
|
395
|
+
|
|
396
|
+
@safe_repo
|
|
397
|
+
def create(self, environment: Environment) -> RepoResp:
|
|
398
|
+
with self.db_manager.session_scope() as session:
|
|
399
|
+
sql_env = self._environment_to_sql(environment)
|
|
400
|
+
session.add(sql_env)
|
|
401
|
+
session.flush()
|
|
402
|
+
new_env = self._sql_to_environment(sql_env)
|
|
403
|
+
return RepoResp(data=new_env)
|
|
404
|
+
|
|
405
|
+
@safe_repo
|
|
406
|
+
def update(self, environment: Environment) -> RepoResp:
|
|
407
|
+
with self.db_manager.session_scope() as session:
|
|
408
|
+
sql_environment = session.get(SQLEnvironment, environment.id)
|
|
409
|
+
if not sql_environment:
|
|
410
|
+
return RepoResp(status=RepoStatus.NOT_FOUND)
|
|
411
|
+
|
|
412
|
+
new_data = self._environment_to_sql(environment)
|
|
413
|
+
for field in self._updatable_sql_fields:
|
|
414
|
+
setattr(sql_environment, field, getattr(new_data, field))
|
|
415
|
+
|
|
416
|
+
session.flush()
|
|
417
|
+
|
|
418
|
+
new_environment = self._sql_to_environment(sql_environment)
|
|
419
|
+
return RepoResp(data=new_environment)
|
|
420
|
+
|
|
421
|
+
@safe_repo
|
|
422
|
+
def delete_by_id(self, id: int) -> RepoResp:
|
|
423
|
+
with self.db_manager.session_scope() as session:
|
|
424
|
+
sql_environment = session.get(SQLEnvironment, id)
|
|
425
|
+
if not sql_environment:
|
|
426
|
+
return RepoResp(status=RepoStatus.NOT_FOUND)
|
|
427
|
+
|
|
428
|
+
session.delete(sql_environment)
|
|
429
|
+
return RepoResp()
|
|
430
|
+
|
|
431
|
+
@property
|
|
432
|
+
def _updatable_sql_fields(self) -> list[str]:
|
|
433
|
+
return [SQLEnvironment.name.key, SQLEnvironment.variables.key]
|
|
434
|
+
|
|
435
|
+
def _sql_to_environment(
|
|
436
|
+
self, sql_environment: SQLEnvironment
|
|
437
|
+
) -> Environment:
|
|
438
|
+
return Environment(
|
|
439
|
+
id=sql_environment.id,
|
|
440
|
+
name=sql_environment.name,
|
|
441
|
+
variables=json.loads(sql_environment.variables),
|
|
442
|
+
created_at=sql_environment.created_at.replace(tzinfo=UTC),
|
|
443
|
+
updated_at=sql_environment.updated_at.replace(tzinfo=UTC),
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
def _environment_to_sql(self, environment: Environment) -> SQLEnvironment:
|
|
447
|
+
return SQLEnvironment(
|
|
448
|
+
id=environment.id,
|
|
449
|
+
name=environment.name,
|
|
450
|
+
variables=json.dumps(
|
|
451
|
+
[variable.model_dump() for variable in environment.variables]
|
|
452
|
+
),
|
|
453
|
+
created_at=environment.created_at,
|
|
454
|
+
updated_at=environment.updated_at,
|
|
455
|
+
)
|
restiny/entities.py
CHANGED
|
@@ -124,6 +124,109 @@ class Request(BaseModel):
|
|
|
124
124
|
created_at: datetime | None = None
|
|
125
125
|
updated_at: datetime | None = None
|
|
126
126
|
|
|
127
|
+
def resolve_variables(
|
|
128
|
+
self, variables: list[Environment.Variable]
|
|
129
|
+
) -> 'Request':
|
|
130
|
+
def _resolve_variables(value: str) -> str:
|
|
131
|
+
new_value = value
|
|
132
|
+
for variable in variables:
|
|
133
|
+
if not variable.enabled:
|
|
134
|
+
continue
|
|
135
|
+
|
|
136
|
+
new_value = new_value.replace(
|
|
137
|
+
'{{' + variable.key + '}}', variable.value
|
|
138
|
+
)
|
|
139
|
+
new_value = new_value.replace(
|
|
140
|
+
'${' + variable.key + '}', variable.value
|
|
141
|
+
)
|
|
142
|
+
return new_value
|
|
143
|
+
|
|
144
|
+
resolved_url = _resolve_variables(self.url)
|
|
145
|
+
|
|
146
|
+
resolved_headers = [
|
|
147
|
+
self.Header(
|
|
148
|
+
enabled=header.enabled,
|
|
149
|
+
key=_resolve_variables(header.key),
|
|
150
|
+
value=_resolve_variables(header.value),
|
|
151
|
+
)
|
|
152
|
+
for header in self.headers
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
resolved_params = [
|
|
156
|
+
self.Param(
|
|
157
|
+
enabled=param.enabled,
|
|
158
|
+
key=_resolve_variables(param.key),
|
|
159
|
+
value=_resolve_variables(param.value),
|
|
160
|
+
)
|
|
161
|
+
for param in self.params
|
|
162
|
+
]
|
|
163
|
+
|
|
164
|
+
resolved_auth = self.auth
|
|
165
|
+
if self.auth_enabled:
|
|
166
|
+
if self.auth_mode == AuthMode.BASIC:
|
|
167
|
+
resolved_auth = self.BasicAuth(
|
|
168
|
+
username=_resolve_variables(self.auth.username),
|
|
169
|
+
password=_resolve_variables(self.auth.password),
|
|
170
|
+
)
|
|
171
|
+
elif self.auth_mode == AuthMode.BEARER:
|
|
172
|
+
resolved_auth = self.BearerAuth(
|
|
173
|
+
token=_resolve_variables(self.auth.token)
|
|
174
|
+
)
|
|
175
|
+
elif self.auth_mode == AuthMode.API_KEY:
|
|
176
|
+
resolved_auth = self.ApiKeyAuth(
|
|
177
|
+
key=_resolve_variables(self.auth.key),
|
|
178
|
+
value=_resolve_variables(self.auth.value),
|
|
179
|
+
where=self.auth.where,
|
|
180
|
+
)
|
|
181
|
+
elif self.auth_mode == AuthMode.DIGEST:
|
|
182
|
+
resolved_auth = self.DigestAuth(
|
|
183
|
+
username=_resolve_variables(self.auth.username),
|
|
184
|
+
password=_resolve_variables(self.auth.password),
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
resolved_body = self.body
|
|
188
|
+
if self.body_enabled:
|
|
189
|
+
if self.body_mode == BodyMode.RAW:
|
|
190
|
+
resolved_body = self.RawBody(
|
|
191
|
+
language=self.body.language,
|
|
192
|
+
value=_resolve_variables(self.body.value),
|
|
193
|
+
)
|
|
194
|
+
elif self.body_mode == BodyMode.FILE:
|
|
195
|
+
pass
|
|
196
|
+
elif self.body_mode == BodyMode.FORM_URLENCODED:
|
|
197
|
+
resolved_body = self.UrlEncodedFormBody(
|
|
198
|
+
fields=[
|
|
199
|
+
self.UrlEncodedFormBody.Field(
|
|
200
|
+
enabled=field.enabled,
|
|
201
|
+
key=_resolve_variables(field.key),
|
|
202
|
+
value=_resolve_variables(field.value),
|
|
203
|
+
)
|
|
204
|
+
for field in self.body.fields
|
|
205
|
+
]
|
|
206
|
+
)
|
|
207
|
+
elif self.body_mode == BodyMode.FORM_MULTIPART:
|
|
208
|
+
resolved_body = self.MultipartFormBody(
|
|
209
|
+
fields=[
|
|
210
|
+
self.MultipartFormBody.Field(
|
|
211
|
+
value_kind=field.value_kind,
|
|
212
|
+
enabled=field.enabled,
|
|
213
|
+
key=_resolve_variables(field.key),
|
|
214
|
+
value=_resolve_variables(field.value),
|
|
215
|
+
)
|
|
216
|
+
for field in self.body.fields
|
|
217
|
+
]
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
return self.model_copy(
|
|
221
|
+
update=dict(
|
|
222
|
+
url=resolved_url,
|
|
223
|
+
headers=resolved_headers,
|
|
224
|
+
params=resolved_params,
|
|
225
|
+
body=resolved_body,
|
|
226
|
+
auth=resolved_auth,
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
|
|
127
230
|
def to_httpx_req(self) -> httpx.Request:
|
|
128
231
|
headers: dict[str, str] = {
|
|
129
232
|
header.key: header.value
|
|
@@ -260,7 +363,7 @@ class Request(BaseModel):
|
|
|
260
363
|
body_files = None
|
|
261
364
|
if self.body_enabled:
|
|
262
365
|
if self.body_mode == BodyMode.RAW:
|
|
263
|
-
body_raw = self.body
|
|
366
|
+
body_raw = self.body.value
|
|
264
367
|
elif self.body_mode == BodyMode.FORM_URLENCODED:
|
|
265
368
|
body_form_urlencoded = {
|
|
266
369
|
form_field.key: form_field.value
|
|
@@ -318,3 +421,18 @@ class Settings(BaseModel):
|
|
|
318
421
|
|
|
319
422
|
created_at: datetime | None = None
|
|
320
423
|
updated_at: datetime | None = None
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
class Environment(BaseModel):
|
|
427
|
+
class Variable(BaseModel):
|
|
428
|
+
enabled: bool
|
|
429
|
+
key: str
|
|
430
|
+
value: str
|
|
431
|
+
|
|
432
|
+
id: int | None = None
|
|
433
|
+
|
|
434
|
+
name: str
|
|
435
|
+
variables: list[Variable] = _Field(default_factory=list)
|
|
436
|
+
|
|
437
|
+
created_at: datetime | None = None
|
|
438
|
+
updated_at: datetime | None = None
|
restiny/ui/__init__.py
CHANGED
|
@@ -5,6 +5,7 @@ This module contains the specific sections of the DataFox user interface (UI).
|
|
|
5
5
|
from restiny.ui.collections_area import CollectionsArea
|
|
6
6
|
from restiny.ui.request_area import RequestArea
|
|
7
7
|
from restiny.ui.response_area import ResponseArea
|
|
8
|
+
from restiny.ui.top_bar_area import TopBarArea
|
|
8
9
|
from restiny.ui.url_area import URLArea
|
|
9
10
|
|
|
10
11
|
__all__ = [
|
|
@@ -12,4 +13,5 @@ __all__ = [
|
|
|
12
13
|
'ResponseArea',
|
|
13
14
|
'URLArea',
|
|
14
15
|
'CollectionsArea',
|
|
16
|
+
'TopBarArea',
|
|
15
17
|
]
|