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
restiny/entities.py
ADDED
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import mimetypes
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, Literal
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
from pydantic import BaseModel, field_validator
|
|
9
|
+
from pydantic import Field as _Field
|
|
10
|
+
from pydantic_core.core_schema import ValidationInfo
|
|
11
|
+
|
|
12
|
+
from restiny import httpx_auths
|
|
13
|
+
from restiny.enums import (
|
|
14
|
+
AuthMode,
|
|
15
|
+
BodyMode,
|
|
16
|
+
BodyRawLanguage,
|
|
17
|
+
ContentType,
|
|
18
|
+
CustomThemes,
|
|
19
|
+
HTTPMethod,
|
|
20
|
+
)
|
|
21
|
+
from restiny.utils import build_curl_cmd
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Folder(BaseModel):
|
|
25
|
+
id: int | None = None
|
|
26
|
+
|
|
27
|
+
name: str
|
|
28
|
+
parent_id: int | None = None
|
|
29
|
+
|
|
30
|
+
created_at: datetime | None = None
|
|
31
|
+
updated_at: datetime | None = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Request(BaseModel):
|
|
35
|
+
class Header(BaseModel):
|
|
36
|
+
enabled: bool
|
|
37
|
+
key: str
|
|
38
|
+
value: str
|
|
39
|
+
|
|
40
|
+
class Param(BaseModel):
|
|
41
|
+
enabled: bool
|
|
42
|
+
key: str
|
|
43
|
+
value: str
|
|
44
|
+
|
|
45
|
+
class RawBody(BaseModel):
|
|
46
|
+
language: BodyRawLanguage
|
|
47
|
+
value: str
|
|
48
|
+
|
|
49
|
+
class FileBody(BaseModel):
|
|
50
|
+
file: Path | None
|
|
51
|
+
|
|
52
|
+
class UrlEncodedFormBody(BaseModel):
|
|
53
|
+
class Field(BaseModel):
|
|
54
|
+
enabled: bool
|
|
55
|
+
key: str
|
|
56
|
+
value: str
|
|
57
|
+
|
|
58
|
+
fields: list[Field]
|
|
59
|
+
|
|
60
|
+
class MultipartFormBody(BaseModel):
|
|
61
|
+
class Field(BaseModel):
|
|
62
|
+
value_kind: Literal['text', 'file']
|
|
63
|
+
enabled: bool
|
|
64
|
+
key: str
|
|
65
|
+
value: str | Path | None
|
|
66
|
+
|
|
67
|
+
@field_validator('value', mode='before')
|
|
68
|
+
@classmethod
|
|
69
|
+
def validate_value(cls, value: Any, info: ValidationInfo):
|
|
70
|
+
if value is None:
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
kind = info.data.get('value_kind')
|
|
74
|
+
if kind == 'file':
|
|
75
|
+
return Path(value)
|
|
76
|
+
elif kind == 'text':
|
|
77
|
+
return str(value)
|
|
78
|
+
|
|
79
|
+
fields: list[Field]
|
|
80
|
+
|
|
81
|
+
class BasicAuth(BaseModel):
|
|
82
|
+
username: str
|
|
83
|
+
password: str
|
|
84
|
+
|
|
85
|
+
class BearerAuth(BaseModel):
|
|
86
|
+
token: str
|
|
87
|
+
|
|
88
|
+
class ApiKeyAuth(BaseModel):
|
|
89
|
+
key: str
|
|
90
|
+
value: str
|
|
91
|
+
where: Literal['header', 'param']
|
|
92
|
+
|
|
93
|
+
class DigestAuth(BaseModel):
|
|
94
|
+
username: str
|
|
95
|
+
password: str
|
|
96
|
+
|
|
97
|
+
class Options(BaseModel):
|
|
98
|
+
timeout: float = 5.5
|
|
99
|
+
follow_redirects: bool = True
|
|
100
|
+
verify_ssl: bool = True
|
|
101
|
+
|
|
102
|
+
id: int | None = None
|
|
103
|
+
|
|
104
|
+
folder_id: int
|
|
105
|
+
name: str
|
|
106
|
+
|
|
107
|
+
method: HTTPMethod = HTTPMethod.GET
|
|
108
|
+
url: str = ''
|
|
109
|
+
headers: list[Header] = _Field(default_factory=list)
|
|
110
|
+
params: list[Param] = _Field(default_factory=list)
|
|
111
|
+
|
|
112
|
+
body_enabled: bool = False
|
|
113
|
+
body_mode: str = BodyMode.RAW
|
|
114
|
+
body: (
|
|
115
|
+
RawBody | FileBody | UrlEncodedFormBody | MultipartFormBody | None
|
|
116
|
+
) = None
|
|
117
|
+
|
|
118
|
+
auth_enabled: bool = False
|
|
119
|
+
auth_mode: AuthMode = AuthMode.BASIC
|
|
120
|
+
auth: BasicAuth | BearerAuth | ApiKeyAuth | DigestAuth | None = None
|
|
121
|
+
|
|
122
|
+
options: Options = _Field(default_factory=Options)
|
|
123
|
+
|
|
124
|
+
created_at: datetime | None = None
|
|
125
|
+
updated_at: datetime | None = None
|
|
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
|
+
|
|
230
|
+
def to_httpx_req(self) -> httpx.Request:
|
|
231
|
+
headers: dict[str, str] = {
|
|
232
|
+
header.key: header.value
|
|
233
|
+
for header in self.headers
|
|
234
|
+
if header.enabled
|
|
235
|
+
}
|
|
236
|
+
params: dict[str, str] = {
|
|
237
|
+
param.key: param.value for param in self.params if param.enabled
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if not self.body_enabled:
|
|
241
|
+
return httpx.Request(
|
|
242
|
+
method=self.method,
|
|
243
|
+
url=self.url,
|
|
244
|
+
headers=headers,
|
|
245
|
+
params=params,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
if self.body_mode == BodyMode.RAW:
|
|
249
|
+
raw_language_to_content_type = {
|
|
250
|
+
BodyRawLanguage.JSON: ContentType.JSON,
|
|
251
|
+
BodyRawLanguage.YAML: ContentType.YAML,
|
|
252
|
+
BodyRawLanguage.HTML: ContentType.HTML,
|
|
253
|
+
BodyRawLanguage.XML: ContentType.XML,
|
|
254
|
+
BodyRawLanguage.PLAIN: ContentType.TEXT,
|
|
255
|
+
}
|
|
256
|
+
headers['content-type'] = raw_language_to_content_type.get(
|
|
257
|
+
self.body.language, ContentType.TEXT
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
raw = self.body.value
|
|
261
|
+
if headers['content-type'] == ContentType.JSON:
|
|
262
|
+
try:
|
|
263
|
+
raw = json.dumps(raw)
|
|
264
|
+
except Exception:
|
|
265
|
+
pass
|
|
266
|
+
|
|
267
|
+
return httpx.Request(
|
|
268
|
+
method=self.method,
|
|
269
|
+
url=self.url,
|
|
270
|
+
headers=headers,
|
|
271
|
+
params=params,
|
|
272
|
+
content=raw,
|
|
273
|
+
)
|
|
274
|
+
elif self.body_mode == BodyMode.FILE:
|
|
275
|
+
file = self.body.file
|
|
276
|
+
if 'content-type' not in headers:
|
|
277
|
+
headers['content-type'] = (
|
|
278
|
+
mimetypes.guess_type(file.name)[0]
|
|
279
|
+
or 'application/octet-stream'
|
|
280
|
+
)
|
|
281
|
+
return httpx.Request(
|
|
282
|
+
method=self.method,
|
|
283
|
+
url=self.url,
|
|
284
|
+
headers=headers,
|
|
285
|
+
params=params,
|
|
286
|
+
content=file.read_bytes(),
|
|
287
|
+
)
|
|
288
|
+
elif self.body_mode == BodyMode.FORM_URLENCODED:
|
|
289
|
+
form_urlencoded = {
|
|
290
|
+
form_item.key: form_item.value
|
|
291
|
+
for form_item in self.body.fields
|
|
292
|
+
if form_item.enabled
|
|
293
|
+
}
|
|
294
|
+
return httpx.Request(
|
|
295
|
+
method=self.method,
|
|
296
|
+
url=self.url,
|
|
297
|
+
headers=headers,
|
|
298
|
+
params=params,
|
|
299
|
+
data=form_urlencoded,
|
|
300
|
+
)
|
|
301
|
+
elif self.body_mode == BodyMode.FORM_MULTIPART:
|
|
302
|
+
form_multipart_str = {
|
|
303
|
+
form_item.key: form_item.value
|
|
304
|
+
for form_item in self.body.fields
|
|
305
|
+
if form_item.enabled and isinstance(form_item.value, str)
|
|
306
|
+
}
|
|
307
|
+
form_multipart_files = {
|
|
308
|
+
form_item.key: (
|
|
309
|
+
form_item.value.name,
|
|
310
|
+
form_item.value.read_bytes(),
|
|
311
|
+
mimetypes.guess_type(form_item.value.name)[0]
|
|
312
|
+
or 'application/octet-stream',
|
|
313
|
+
)
|
|
314
|
+
for form_item in self.body.fields
|
|
315
|
+
if form_item.enabled and isinstance(form_item.value, Path)
|
|
316
|
+
}
|
|
317
|
+
return httpx.Request(
|
|
318
|
+
method=self.method,
|
|
319
|
+
url=self.url,
|
|
320
|
+
headers=headers,
|
|
321
|
+
params=params,
|
|
322
|
+
data=form_multipart_str,
|
|
323
|
+
files=form_multipart_files,
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
def to_httpx_auth(self) -> httpx.Auth | None:
|
|
327
|
+
if not self.auth_enabled:
|
|
328
|
+
return
|
|
329
|
+
|
|
330
|
+
if self.auth_mode == AuthMode.BASIC:
|
|
331
|
+
return httpx.BasicAuth(
|
|
332
|
+
username=self.auth.username, password=self.auth.password
|
|
333
|
+
)
|
|
334
|
+
elif self.auth_mode == AuthMode.BEARER:
|
|
335
|
+
return httpx_auths.BearerAuth(token=self.auth.token)
|
|
336
|
+
elif self.auth_mode == AuthMode.API_KEY:
|
|
337
|
+
if self.auth.where == 'header':
|
|
338
|
+
return httpx_auths.APIKeyHeaderAuth(
|
|
339
|
+
key=self.auth.key, value=self.auth.value
|
|
340
|
+
)
|
|
341
|
+
elif self.auth.where == 'param':
|
|
342
|
+
return httpx_auths.APIKeyParamAuth(
|
|
343
|
+
key=self.auth.key, value=self.auth.value
|
|
344
|
+
)
|
|
345
|
+
elif self.auth_mode == AuthMode.DIGEST:
|
|
346
|
+
return httpx.DigestAuth(
|
|
347
|
+
username=self.auth.username, password=self.auth.password
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
def to_curl(self) -> str:
|
|
351
|
+
headers: dict[str, str] = {
|
|
352
|
+
header.key: header.value
|
|
353
|
+
for header in self.headers
|
|
354
|
+
if header.enabled
|
|
355
|
+
}
|
|
356
|
+
params: dict[str, str] = {
|
|
357
|
+
param.key: param.value for param in self.params if param.enabled
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
body_raw = None
|
|
361
|
+
body_form_urlencoded = None
|
|
362
|
+
body_form_multipart = None
|
|
363
|
+
body_files = None
|
|
364
|
+
if self.body_enabled:
|
|
365
|
+
if self.body_mode == BodyMode.RAW:
|
|
366
|
+
body_raw = self.body.value
|
|
367
|
+
elif self.body_mode == BodyMode.FORM_URLENCODED:
|
|
368
|
+
body_form_urlencoded = {
|
|
369
|
+
form_field.key: form_field.value
|
|
370
|
+
for form_field in self.body.fields
|
|
371
|
+
if form_field.enabled
|
|
372
|
+
}
|
|
373
|
+
elif self.body_mode == BodyMode.FORM_MULTIPART:
|
|
374
|
+
body_form_multipart = {
|
|
375
|
+
form_field.key: form_field.value
|
|
376
|
+
for form_field in self.body.fields
|
|
377
|
+
if form_field.enabled
|
|
378
|
+
}
|
|
379
|
+
elif self.body_mode == BodyMode.FILE:
|
|
380
|
+
body_files = [self.body]
|
|
381
|
+
|
|
382
|
+
auth_basic = None
|
|
383
|
+
auth_bearer = None
|
|
384
|
+
auth_api_key_header = None
|
|
385
|
+
auth_api_key_param = None
|
|
386
|
+
auth_digest = None
|
|
387
|
+
if self.auth_enabled:
|
|
388
|
+
if self.auth_mode == AuthMode.BASIC:
|
|
389
|
+
auth_basic = (self.auth.username, self.auth.password)
|
|
390
|
+
elif self.auth_mode == AuthMode.BEARER:
|
|
391
|
+
auth_bearer = self.auth.token
|
|
392
|
+
elif self.auth_mode == AuthMode.API_KEY:
|
|
393
|
+
if self.auth.where == 'header':
|
|
394
|
+
auth_api_key_header = (self.auth.key, self.auth.value)
|
|
395
|
+
elif self.auth.where == 'param':
|
|
396
|
+
auth_api_key_param = (self.auth.key, self.auth.value)
|
|
397
|
+
elif self.auth_mode == AuthMode.DIGEST:
|
|
398
|
+
auth_digest = (self.auth.username, self.auth.password)
|
|
399
|
+
|
|
400
|
+
return build_curl_cmd(
|
|
401
|
+
method=self.method,
|
|
402
|
+
url=self.url,
|
|
403
|
+
headers=headers,
|
|
404
|
+
params=params,
|
|
405
|
+
body_raw=body_raw,
|
|
406
|
+
body_form_urlencoded=body_form_urlencoded,
|
|
407
|
+
body_form_multipart=body_form_multipart,
|
|
408
|
+
body_files=body_files,
|
|
409
|
+
auth_basic=auth_basic,
|
|
410
|
+
auth_bearer=auth_bearer,
|
|
411
|
+
auth_api_key_header=auth_api_key_header,
|
|
412
|
+
auth_api_key_param=auth_api_key_param,
|
|
413
|
+
auth_digest=auth_digest,
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
class Settings(BaseModel):
|
|
418
|
+
id: int | None = None
|
|
419
|
+
|
|
420
|
+
theme: CustomThemes = CustomThemes.DARK
|
|
421
|
+
|
|
422
|
+
created_at: datetime | None = None
|
|
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/enums.py
CHANGED
|
@@ -6,17 +6,13 @@ class HTTPMethod(StrEnum):
|
|
|
6
6
|
GET = 'GET'
|
|
7
7
|
POST = 'POST'
|
|
8
8
|
PUT = 'PUT'
|
|
9
|
+
PATCH = 'PATCH'
|
|
9
10
|
DELETE = 'DELETE'
|
|
10
11
|
HEAD = 'HEAD'
|
|
11
12
|
OPTIONS = 'OPTIONS'
|
|
12
|
-
PATCH = 'PATCH'
|
|
13
13
|
CONNECT = 'CONNECT'
|
|
14
14
|
TRACE = 'TRACE'
|
|
15
15
|
|
|
16
|
-
@classmethod
|
|
17
|
-
def values(cls):
|
|
18
|
-
return [method.value for method in cls]
|
|
19
|
-
|
|
20
16
|
|
|
21
17
|
class BodyMode(StrEnum):
|
|
22
18
|
RAW = 'raw'
|
|
@@ -41,3 +37,16 @@ class ContentType(StrEnum):
|
|
|
41
37
|
XML = 'application/xml'
|
|
42
38
|
FORM_URLENCODED = 'application/x-www-form-urlencoded'
|
|
43
39
|
FORM_MULTIPART = 'multipart/form-data'
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class AuthMode(StrEnum):
|
|
43
|
+
BASIC = 'basic'
|
|
44
|
+
BEARER = 'bearer'
|
|
45
|
+
API_KEY = 'api_key'
|
|
46
|
+
DIGEST = 'digest'
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class CustomThemes(StrEnum):
|
|
50
|
+
DARK = 'dark'
|
|
51
|
+
DRACULA = 'dracula'
|
|
52
|
+
FOREST = 'forest'
|
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/ui/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains the specific sections of the DataFox user interface (UI).
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from restiny.ui.collections_area import CollectionsArea
|
|
6
|
+
from restiny.ui.request_area import RequestArea
|
|
7
|
+
from restiny.ui.response_area import ResponseArea
|
|
8
|
+
from restiny.ui.top_bar_area import TopBarArea
|
|
9
|
+
from restiny.ui.url_area import URLArea
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
'RequestArea',
|
|
13
|
+
'ResponseArea',
|
|
14
|
+
'URLArea',
|
|
15
|
+
'CollectionsArea',
|
|
16
|
+
'TopBarArea',
|
|
17
|
+
]
|