restiny 0.2.1__py3-none-any.whl → 0.5.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.
restiny/data/repos.py ADDED
@@ -0,0 +1,351 @@
1
+ import json
2
+ import sys
3
+ import traceback
4
+ from abc import ABC, abstractmethod
5
+ from dataclasses import dataclass
6
+ from datetime import UTC
7
+ from enum import StrEnum
8
+ from functools import wraps
9
+ from typing import Any
10
+
11
+ from sqlalchemy import select
12
+ from sqlalchemy.exc import IntegrityError, InterfaceError, OperationalError
13
+
14
+ from restiny.data.db import DBManager
15
+ from restiny.data.models import SQLFolder, SQLRequest, SQLSettings
16
+ from restiny.entities import Folder, Request, Settings
17
+
18
+
19
+ def safe_repo(func):
20
+ @wraps(func)
21
+ def wrapper(*args, **kwargs):
22
+ try:
23
+ return func(*args, **kwargs)
24
+
25
+ except (
26
+ InterfaceError,
27
+ OperationalError,
28
+ ):
29
+ traceback.print_exc(file=sys.stderr)
30
+ return RepoResp(status=RepoStatus.DB_ERROR)
31
+
32
+ except IntegrityError as error:
33
+ error_msg = str(error)
34
+ if 'UNIQUE' in str(error_msg):
35
+ return RepoResp(status=RepoStatus.DUPLICATED)
36
+
37
+ traceback.print_exc(file=sys.stderr)
38
+ return RepoResp(status=RepoStatus.DB_ERROR)
39
+
40
+ return wrapper
41
+
42
+
43
+ class RepoStatus(StrEnum):
44
+ OK = 'ok'
45
+ NOT_FOUND = 'not_found'
46
+ DUPLICATED = 'duplicated'
47
+ DB_ERROR = 'db_error'
48
+
49
+
50
+ @dataclass
51
+ class RepoResp:
52
+ status: RepoStatus = RepoStatus.OK
53
+ data: Any = None
54
+
55
+ @property
56
+ def ok(self) -> bool:
57
+ return self.status == RepoStatus.OK
58
+
59
+
60
+ class SQLRepoBase(ABC):
61
+ def __init__(self, db_manager: DBManager):
62
+ self.db_manager = db_manager
63
+
64
+ @property
65
+ @abstractmethod
66
+ def _updatable_sql_fields(self) -> list[str]:
67
+ pass
68
+
69
+
70
+ class FoldersSQLRepo(SQLRepoBase):
71
+ @property
72
+ def _updatable_sql_fields(self) -> list[str]:
73
+ return [SQLFolder.parent_id.key, SQLFolder.name.key]
74
+
75
+ @safe_repo
76
+ def list_by_parent_id(self, parent_id: int) -> RepoResp:
77
+ with self.db_manager.session_scope() as session:
78
+ sql_folders = session.scalars(
79
+ select(SQLFolder)
80
+ .where(SQLFolder.parent_id == parent_id)
81
+ .order_by(SQLFolder.name.asc())
82
+ ).all()
83
+ folders = [
84
+ self._sql_to_folder(sql_folder) for sql_folder in sql_folders
85
+ ]
86
+ return RepoResp(data=folders)
87
+
88
+ @safe_repo
89
+ def list_roots(self) -> RepoResp:
90
+ with self.db_manager.session_scope() as session:
91
+ sql_folders = session.scalars(
92
+ select(SQLFolder)
93
+ .where(SQLFolder.parent_id.is_(None))
94
+ .order_by(SQLFolder.name.asc())
95
+ ).all()
96
+ folders = [
97
+ self._sql_to_folder(sql_folder) for sql_folder in sql_folders
98
+ ]
99
+ return RepoResp(data=folders)
100
+
101
+ @safe_repo
102
+ def get_by_id(self, id: int) -> RepoResp:
103
+ with self.db_manager.session_scope() as session:
104
+ sql_folder = session.get(SQLFolder, id)
105
+ if not sql_folder:
106
+ return RepoResp(status=RepoStatus.NOT_FOUND)
107
+
108
+ folder = self._sql_to_folder(sql_folder)
109
+ return RepoResp(data=folder)
110
+
111
+ @safe_repo
112
+ def create(self, folder: Folder) -> RepoResp:
113
+ with self.db_manager.session_scope() as session:
114
+ sql_folder = self._folder_to_sql(folder)
115
+ session.add(sql_folder)
116
+ session.flush()
117
+ new_folder = self._sql_to_folder(sql_folder)
118
+ return RepoResp(data=new_folder)
119
+
120
+ @safe_repo
121
+ def update(self, folder: Folder) -> RepoResp:
122
+ with self.db_manager.session_scope() as session:
123
+ sql_folder = session.get(SQLFolder, folder.id)
124
+ if not sql_folder:
125
+ return RepoResp(status=RepoStatus.NOT_FOUND)
126
+
127
+ new_data = self._folder_to_sql(folder)
128
+ for field in self._updatable_sql_fields:
129
+ setattr(sql_folder, field, getattr(new_data, field))
130
+
131
+ session.flush()
132
+
133
+ new_folder = self._sql_to_folder(sql_folder)
134
+ return RepoResp(data=new_folder)
135
+
136
+ @safe_repo
137
+ def delete_by_id(self, id: int) -> RepoResp:
138
+ with self.db_manager.session_scope() as session:
139
+ sql_folder = session.get(SQLFolder, id)
140
+ if not sql_folder:
141
+ return RepoResp(status=RepoStatus.NOT_FOUND)
142
+
143
+ session.delete(sql_folder)
144
+ return RepoResp()
145
+
146
+ def _sql_to_folder(self, sql_folder: SQLFolder) -> Folder:
147
+ return Folder(
148
+ id=sql_folder.id,
149
+ parent_id=sql_folder.parent_id,
150
+ name=sql_folder.name,
151
+ created_at=sql_folder.created_at.replace(tzinfo=UTC),
152
+ updated_at=sql_folder.updated_at.replace(tzinfo=UTC),
153
+ )
154
+
155
+ def _folder_to_sql(self, folder: Folder) -> SQLFolder:
156
+ return SQLFolder(
157
+ id=folder.id,
158
+ parent_id=folder.parent_id,
159
+ name=folder.name,
160
+ created_at=folder.created_at,
161
+ updated_at=folder.updated_at,
162
+ )
163
+
164
+
165
+ class RequestsSQLRepo(SQLRepoBase):
166
+ @safe_repo
167
+ def list_by_folder_id(self, folder_id: int) -> RepoResp:
168
+ with self.db_manager.session_scope() as session:
169
+ sql_requests = session.scalars(
170
+ select(SQLRequest)
171
+ .where(SQLRequest.folder_id == folder_id)
172
+ .order_by(SQLRequest.name.asc())
173
+ ).all()
174
+ requests = [
175
+ self._sql_to_request(sql_folder) for sql_folder in sql_requests
176
+ ]
177
+ return RepoResp(data=requests)
178
+
179
+ @safe_repo
180
+ def get_by_id(self, id: int) -> RepoResp:
181
+ with self.db_manager.session_scope() as session:
182
+ sql_request = session.get(SQLRequest, id)
183
+
184
+ if not sql_request:
185
+ return RepoResp(status=RepoStatus.NOT_FOUND)
186
+
187
+ request = self._sql_to_request(sql_request)
188
+ return RepoResp(data=request)
189
+
190
+ @safe_repo
191
+ def create(self, request: Request) -> RepoResp:
192
+ with self.db_manager.session_scope() as session:
193
+ sql_request = self._request_to_sql(request)
194
+ session.add(sql_request)
195
+ session.flush()
196
+ new_request = self._sql_to_request(sql_request)
197
+ return RepoResp(data=new_request)
198
+
199
+ @safe_repo
200
+ def update(self, request: Request) -> RepoResp:
201
+ with self.db_manager.session_scope() as session:
202
+ sql_request = session.get(SQLRequest, request.id)
203
+ if not sql_request:
204
+ return RepoResp(status=RepoStatus.NOT_FOUND)
205
+
206
+ new_data = self._request_to_sql(request)
207
+ for field in self._updatable_sql_fields:
208
+ setattr(sql_request, field, getattr(new_data, field))
209
+
210
+ session.flush()
211
+
212
+ new_request = self._sql_to_request(sql_request)
213
+ return RepoResp(data=new_request)
214
+
215
+ @safe_repo
216
+ def delete_by_id(self, id: int) -> RepoResp:
217
+ with self.db_manager.session_scope() as session:
218
+ sql_request = session.get(SQLRequest, id)
219
+ if not sql_request:
220
+ return RepoResp(status=RepoStatus.NOT_FOUND)
221
+
222
+ session.delete(sql_request)
223
+ return RepoResp()
224
+
225
+ @property
226
+ def _updatable_sql_fields(self) -> list[str]:
227
+ return [
228
+ SQLRequest.folder_id.key,
229
+ SQLRequest.name.key,
230
+ SQLRequest.method.key,
231
+ SQLRequest.url.key,
232
+ SQLRequest.headers.key,
233
+ SQLRequest.params.key,
234
+ SQLRequest.body_enabled.key,
235
+ SQLRequest.body_mode.key,
236
+ SQLRequest.body.key,
237
+ SQLRequest.auth_enabled.key,
238
+ SQLRequest.auth_mode.key,
239
+ SQLRequest.auth.key,
240
+ SQLRequest.option_timeout.key,
241
+ SQLRequest.option_follow_redirects.key,
242
+ SQLRequest.option_verify_ssl.key,
243
+ ]
244
+
245
+ def _sql_to_request(self, sql_request: SQLRequest) -> Request:
246
+ return Request(
247
+ id=sql_request.id,
248
+ folder_id=sql_request.folder_id,
249
+ name=sql_request.name,
250
+ method=sql_request.method,
251
+ url=sql_request.url,
252
+ headers=json.loads(sql_request.headers),
253
+ params=json.loads(sql_request.params),
254
+ body_enabled=sql_request.body_enabled,
255
+ body_mode=sql_request.body_mode,
256
+ body=json.loads(sql_request.body) if sql_request.body else None,
257
+ auth_enabled=sql_request.auth_enabled,
258
+ auth_mode=sql_request.auth_mode,
259
+ auth=json.loads(sql_request.auth) if sql_request.auth else None,
260
+ options=Request.Options(
261
+ timeout=sql_request.option_timeout,
262
+ follow_redirects=sql_request.option_follow_redirects,
263
+ verify_ssl=sql_request.option_verify_ssl,
264
+ ),
265
+ created_at=sql_request.created_at.replace(tzinfo=UTC),
266
+ updated_at=sql_request.updated_at.replace(tzinfo=UTC),
267
+ )
268
+
269
+ def _request_to_sql(self, request: Request) -> SQLRequest:
270
+ return SQLRequest(
271
+ id=request.id,
272
+ folder_id=request.folder_id,
273
+ name=request.name,
274
+ method=request.method,
275
+ url=request.url,
276
+ headers=json.dumps(
277
+ [header.model_dump() for header in request.headers]
278
+ ),
279
+ params=json.dumps(
280
+ [param.model_dump() for param in request.params]
281
+ ),
282
+ body_enabled=request.body_enabled,
283
+ body_mode=request.body_mode,
284
+ body=json.dumps(request.body.model_dump(), default=str)
285
+ if request.body
286
+ else None,
287
+ auth_enabled=request.auth_enabled,
288
+ auth_mode=request.auth_mode,
289
+ auth=json.dumps(request.auth.model_dump(), default=str)
290
+ if request.auth
291
+ else None,
292
+ option_timeout=request.options.timeout,
293
+ option_follow_redirects=request.options.follow_redirects,
294
+ option_verify_ssl=request.options.verify_ssl,
295
+ created_at=request.created_at,
296
+ updated_at=request.updated_at,
297
+ )
298
+
299
+
300
+ class SettingsSQLRepo(SQLRepoBase):
301
+ @safe_repo
302
+ def get(self) -> RepoResp:
303
+ with self.db_manager.session_scope() as session:
304
+ sql_settings = session.scalar(select(SQLSettings).limit(1))
305
+
306
+ if not sql_settings:
307
+ return RepoResp(data=Settings())
308
+
309
+ settings = self._sql_to_settings(sql_settings)
310
+ return RepoResp(data=settings)
311
+
312
+ @safe_repo
313
+ def set(self, settings: Settings) -> RepoResp:
314
+ with self.db_manager.session_scope() as session:
315
+ sql_settings = session.scalar(select(SQLSettings).limit(1))
316
+
317
+ if not sql_settings:
318
+ # create
319
+ sql_settings = self._settings_to_sql(settings=settings)
320
+ session.add(sql_settings)
321
+ session.flush()
322
+ new_settings = self._sql_to_settings(sql_settings=sql_settings)
323
+ return RepoResp(data=new_settings)
324
+ else:
325
+ # update
326
+ new_data = self._settings_to_sql(settings=settings)
327
+ for field in self._updatable_sql_fields:
328
+ setattr(sql_settings, field, getattr(new_data, field))
329
+ session.flush()
330
+ new_settings = self._sql_to_settings(sql_settings=sql_settings)
331
+ return RepoResp(data=new_settings)
332
+
333
+ @property
334
+ def _updatable_sql_fields(self) -> list[str]:
335
+ return [SQLSettings.theme.key]
336
+
337
+ def _sql_to_settings(self, sql_settings: SQLSettings) -> Settings:
338
+ return Settings(
339
+ id=sql_settings.id,
340
+ theme=sql_settings.theme,
341
+ created_at=sql_settings.created_at.replace(tzinfo=UTC),
342
+ updated_at=sql_settings.updated_at.replace(tzinfo=UTC),
343
+ )
344
+
345
+ def _settings_to_sql(self, settings: Settings) -> SQLSettings:
346
+ return SQLSettings(
347
+ id=settings.id,
348
+ theme=settings.theme,
349
+ created_at=settings.created_at,
350
+ updated_at=settings.updated_at,
351
+ )
@@ -0,0 +1,3 @@
1
+ from restiny.consts import MODULE_DIR
2
+
3
+ SQL_DIR = MODULE_DIR.joinpath('data/sql')
restiny/entities.py ADDED
@@ -0,0 +1,320 @@
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 to_httpx_req(self) -> httpx.Request:
128
+ headers: dict[str, str] = {
129
+ header.key: header.value
130
+ for header in self.headers
131
+ if header.enabled
132
+ }
133
+ params: dict[str, str] = {
134
+ param.key: param.value for param in self.params if param.enabled
135
+ }
136
+
137
+ if not self.body_enabled:
138
+ return httpx.Request(
139
+ method=self.method,
140
+ url=self.url,
141
+ headers=headers,
142
+ params=params,
143
+ )
144
+
145
+ if self.body_mode == BodyMode.RAW:
146
+ raw_language_to_content_type = {
147
+ BodyRawLanguage.JSON: ContentType.JSON,
148
+ BodyRawLanguage.YAML: ContentType.YAML,
149
+ BodyRawLanguage.HTML: ContentType.HTML,
150
+ BodyRawLanguage.XML: ContentType.XML,
151
+ BodyRawLanguage.PLAIN: ContentType.TEXT,
152
+ }
153
+ headers['content-type'] = raw_language_to_content_type.get(
154
+ self.body.language, ContentType.TEXT
155
+ )
156
+
157
+ raw = self.body.value
158
+ if headers['content-type'] == ContentType.JSON:
159
+ try:
160
+ raw = json.dumps(raw)
161
+ except Exception:
162
+ pass
163
+
164
+ return httpx.Request(
165
+ method=self.method,
166
+ url=self.url,
167
+ headers=headers,
168
+ params=params,
169
+ content=raw,
170
+ )
171
+ elif self.body_mode == BodyMode.FILE:
172
+ file = self.body.file
173
+ if 'content-type' not in headers:
174
+ headers['content-type'] = (
175
+ mimetypes.guess_type(file.name)[0]
176
+ or 'application/octet-stream'
177
+ )
178
+ return httpx.Request(
179
+ method=self.method,
180
+ url=self.url,
181
+ headers=headers,
182
+ params=params,
183
+ content=file.read_bytes(),
184
+ )
185
+ elif self.body_mode == BodyMode.FORM_URLENCODED:
186
+ form_urlencoded = {
187
+ form_item.key: form_item.value
188
+ for form_item in self.body.fields
189
+ if form_item.enabled
190
+ }
191
+ return httpx.Request(
192
+ method=self.method,
193
+ url=self.url,
194
+ headers=headers,
195
+ params=params,
196
+ data=form_urlencoded,
197
+ )
198
+ elif self.body_mode == BodyMode.FORM_MULTIPART:
199
+ form_multipart_str = {
200
+ form_item.key: form_item.value
201
+ for form_item in self.body.fields
202
+ if form_item.enabled and isinstance(form_item.value, str)
203
+ }
204
+ form_multipart_files = {
205
+ form_item.key: (
206
+ form_item.value.name,
207
+ form_item.value.read_bytes(),
208
+ mimetypes.guess_type(form_item.value.name)[0]
209
+ or 'application/octet-stream',
210
+ )
211
+ for form_item in self.body.fields
212
+ if form_item.enabled and isinstance(form_item.value, Path)
213
+ }
214
+ return httpx.Request(
215
+ method=self.method,
216
+ url=self.url,
217
+ headers=headers,
218
+ params=params,
219
+ data=form_multipart_str,
220
+ files=form_multipart_files,
221
+ )
222
+
223
+ def to_httpx_auth(self) -> httpx.Auth | None:
224
+ if not self.auth_enabled:
225
+ return
226
+
227
+ if self.auth_mode == AuthMode.BASIC:
228
+ return httpx.BasicAuth(
229
+ username=self.auth.username, password=self.auth.password
230
+ )
231
+ elif self.auth_mode == AuthMode.BEARER:
232
+ return httpx_auths.BearerAuth(token=self.auth.token)
233
+ elif self.auth_mode == AuthMode.API_KEY:
234
+ if self.auth.where == 'header':
235
+ return httpx_auths.APIKeyHeaderAuth(
236
+ key=self.auth.key, value=self.auth.value
237
+ )
238
+ elif self.auth.where == 'param':
239
+ return httpx_auths.APIKeyParamAuth(
240
+ key=self.auth.key, value=self.auth.value
241
+ )
242
+ elif self.auth_mode == AuthMode.DIGEST:
243
+ return httpx.DigestAuth(
244
+ username=self.auth.username, password=self.auth.password
245
+ )
246
+
247
+ def to_curl(self) -> str:
248
+ headers: dict[str, str] = {
249
+ header.key: header.value
250
+ for header in self.headers
251
+ if header.enabled
252
+ }
253
+ params: dict[str, str] = {
254
+ param.key: param.value for param in self.params if param.enabled
255
+ }
256
+
257
+ body_raw = None
258
+ body_form_urlencoded = None
259
+ body_form_multipart = None
260
+ body_files = None
261
+ if self.body_enabled:
262
+ if self.body_mode == BodyMode.RAW:
263
+ body_raw = self.body
264
+ elif self.body_mode == BodyMode.FORM_URLENCODED:
265
+ body_form_urlencoded = {
266
+ form_field.key: form_field.value
267
+ for form_field in self.body.fields
268
+ if form_field.enabled
269
+ }
270
+ elif self.body_mode == BodyMode.FORM_MULTIPART:
271
+ body_form_multipart = {
272
+ form_field.key: form_field.value
273
+ for form_field in self.body.fields
274
+ if form_field.enabled
275
+ }
276
+ elif self.body_mode == BodyMode.FILE:
277
+ body_files = [self.body]
278
+
279
+ auth_basic = None
280
+ auth_bearer = None
281
+ auth_api_key_header = None
282
+ auth_api_key_param = None
283
+ auth_digest = None
284
+ if self.auth_enabled:
285
+ if self.auth_mode == AuthMode.BASIC:
286
+ auth_basic = (self.auth.username, self.auth.password)
287
+ elif self.auth_mode == AuthMode.BEARER:
288
+ auth_bearer = self.auth.token
289
+ elif self.auth_mode == AuthMode.API_KEY:
290
+ if self.auth.where == 'header':
291
+ auth_api_key_header = (self.auth.key, self.auth.value)
292
+ elif self.auth.where == 'param':
293
+ auth_api_key_param = (self.auth.key, self.auth.value)
294
+ elif self.auth_mode == AuthMode.DIGEST:
295
+ auth_digest = (self.auth.username, self.auth.password)
296
+
297
+ return build_curl_cmd(
298
+ method=self.method,
299
+ url=self.url,
300
+ headers=headers,
301
+ params=params,
302
+ body_raw=body_raw,
303
+ body_form_urlencoded=body_form_urlencoded,
304
+ body_form_multipart=body_form_multipart,
305
+ body_files=body_files,
306
+ auth_basic=auth_basic,
307
+ auth_bearer=auth_bearer,
308
+ auth_api_key_header=auth_api_key_header,
309
+ auth_api_key_param=auth_api_key_param,
310
+ auth_digest=auth_digest,
311
+ )
312
+
313
+
314
+ class Settings(BaseModel):
315
+ id: int | None = None
316
+
317
+ theme: CustomThemes = CustomThemes.DARK
318
+
319
+ created_at: datetime | None = None
320
+ 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'