pyrestkit 1.2.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.
- pyrestkit/__init__.py +35 -0
- pyrestkit/ai/__init__.py +17 -0
- pyrestkit/ai/analyzer.py +137 -0
- pyrestkit/ai/client.py +101 -0
- pyrestkit/ai/config/__init__.py +5 -0
- pyrestkit/ai/config/ai_config.py +200 -0
- pyrestkit/ai/exceptions.py +22 -0
- pyrestkit/ai/generators/__init__.py +0 -0
- pyrestkit/ai/models.py +44 -0
- pyrestkit/ai/parsers/__init__.py +0 -0
- pyrestkit/ai/prompts/failure_analysis.md +21 -0
- pyrestkit/ai/provider.py +58 -0
- pyrestkit/ai/providers/__init__.py +21 -0
- pyrestkit/ai/providers/anthropic.py +85 -0
- pyrestkit/ai/providers/azure_openai.py +84 -0
- pyrestkit/ai/providers/base.py +39 -0
- pyrestkit/ai/providers/bedrock.py +70 -0
- pyrestkit/ai/providers/cohere.py +82 -0
- pyrestkit/ai/providers/gemini.py +113 -0
- pyrestkit/ai/providers/groq.py +81 -0
- pyrestkit/ai/providers/mistral.py +88 -0
- pyrestkit/ai/providers/ollama.py +82 -0
- pyrestkit/ai/providers/openai.py +124 -0
- pyrestkit/ai/utils/__init__.py +0 -0
- pyrestkit/ai/utils/prompt_loader.py +52 -0
- pyrestkit/assertions/__init__.py +7 -0
- pyrestkit/assertions/assertion_exception.py +4 -0
- pyrestkit/assertions/response_assertions.py +181 -0
- pyrestkit/auth/__init__.py +11 -0
- pyrestkit/auth/auth_strategy.py +14 -0
- pyrestkit/auth/authentication_manager.py +35 -0
- pyrestkit/auth/strategies/__init__.py +5 -0
- pyrestkit/auth/strategies/api_key_auth.py +18 -0
- pyrestkit/auth/strategies/basic_auth.py +24 -0
- pyrestkit/auth/strategies/bearer_auth.py +17 -0
- pyrestkit/auth/token_cache.py +44 -0
- pyrestkit/auth/token_manager.py +32 -0
- pyrestkit/auth/token_provider.py +12 -0
- pyrestkit/auth/token_response.py +13 -0
- pyrestkit/builder/__init__.py +5 -0
- pyrestkit/builder/fluent_request_builder.py +167 -0
- pyrestkit/clients/__init__.py +7 -0
- pyrestkit/clients/base_client.py +68 -0
- pyrestkit/clients/user_client.py +66 -0
- pyrestkit/config/__init__.py +5 -0
- pyrestkit/config/config.py +97 -0
- pyrestkit/constants/__init__.py +0 -0
- pyrestkit/constants/content_types.py +0 -0
- pyrestkit/constants/headers.py +0 -0
- pyrestkit/constants/status_codes.py +0 -0
- pyrestkit/core/__init__.py +13 -0
- pyrestkit/core/api_client.py +129 -0
- pyrestkit/core/logger.py +41 -0
- pyrestkit/core/request_builder.py +45 -0
- pyrestkit/core/request_executor.py +64 -0
- pyrestkit/core/request_logger.py +0 -0
- pyrestkit/core/response_logger.py +0 -0
- pyrestkit/core/session_manager.py +19 -0
- pyrestkit/database/__init__.py +0 -0
- pyrestkit/endpoints/__init__.py +5 -0
- pyrestkit/endpoints/base_endpoints.py +32 -0
- pyrestkit/endpoints/order_endpoints.py +9 -0
- pyrestkit/endpoints/payment_endpoints.py +5 -0
- pyrestkit/endpoints/user_endpoints.py +48 -0
- pyrestkit/exceptions/__init__.py +21 -0
- pyrestkit/exceptions/api_exception.py +8 -0
- pyrestkit/exceptions/authentication_exception.py +10 -0
- pyrestkit/exceptions/configuration_exception.py +10 -0
- pyrestkit/exceptions/exception_mapper.py +32 -0
- pyrestkit/exceptions/network_exception.py +10 -0
- pyrestkit/exceptions/response_exception.py +10 -0
- pyrestkit/exceptions/serialization_exception.py +10 -0
- pyrestkit/exceptions/validation_exception.py +10 -0
- pyrestkit/factories/__init__.py +5 -0
- pyrestkit/factories/base_factory.py +25 -0
- pyrestkit/factories/user_factory.py +37 -0
- pyrestkit/hooks/__init__.py +5 -0
- pyrestkit/hooks/hook.py +27 -0
- pyrestkit/hooks/hook_manager.py +39 -0
- pyrestkit/hooks/request_hook.py +18 -0
- pyrestkit/hooks/response_hook.py +17 -0
- pyrestkit/hooks/timing_hook.py +32 -0
- pyrestkit/models/__init__.py +8 -0
- pyrestkit/models/base_response.py +11 -0
- pyrestkit/models/request/__init__.py +7 -0
- pyrestkit/models/request/create_user_request.py +11 -0
- pyrestkit/models/request/update_user_request.py +10 -0
- pyrestkit/models/response/__init__.py +5 -0
- pyrestkit/models/response/create_user_response.py +26 -0
- pyrestkit/models/response/get_user_response.py +28 -0
- pyrestkit/models/response/user_response.py +12 -0
- pyrestkit/pipeline/__init__.py +5 -0
- pyrestkit/pipeline/middleware.py +18 -0
- pyrestkit/pipeline/middleware_chain.py +11 -0
- pyrestkit/pipeline/pipeline.py +27 -0
- pyrestkit/pipeline/request_context.py +26 -0
- pyrestkit/response/__init__.py +8 -0
- pyrestkit/response/framework_response.py +271 -0
- pyrestkit/response/response_body.py +124 -0
- pyrestkit/retry/__init__.py +5 -0
- pyrestkit/retry/backoff.py +32 -0
- pyrestkit/retry/retry_handler.py +52 -0
- pyrestkit/retry/retry_policy.py +33 -0
- pyrestkit/serializers/__init__.py +0 -0
- pyrestkit/serializers/response_mapper.py +25 -0
- pyrestkit/types/__init__.py +0 -0
- pyrestkit/types/model_protocol.py +17 -0
- pyrestkit/utils/__init__.py +0 -0
- pyrestkit/validators/__init__.py +7 -0
- pyrestkit/validators/response_validator.py +57 -0
- pyrestkit/validators/schema_validator.py +33 -0
- pyrestkit-1.2.0.dist-info/METADATA +741 -0
- pyrestkit-1.2.0.dist-info/RECORD +115 -0
- pyrestkit-1.2.0.dist-info/WHEEL +5 -0
- pyrestkit-1.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from pyrestkit.config.config import ConfigManager
|
|
6
|
+
from pyrestkit.core.logger import FrameworkLogger
|
|
7
|
+
from pyrestkit.core.session_manager import SessionManager
|
|
8
|
+
from pyrestkit.exceptions.exception_mapper import ExceptionMapper
|
|
9
|
+
from pyrestkit.hooks.hook_manager import HookManager
|
|
10
|
+
from pyrestkit.response.framework_response import FrameworkResponse
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RequestExecutor:
|
|
14
|
+
"""
|
|
15
|
+
Executes HTTP requests using the configured session.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
config: ConfigManager,
|
|
21
|
+
session_manager: SessionManager,
|
|
22
|
+
hook_manager: HookManager | None = None,
|
|
23
|
+
) -> None:
|
|
24
|
+
self._config = config
|
|
25
|
+
self._session = session_manager.session
|
|
26
|
+
self._logger = FrameworkLogger.get_logger()
|
|
27
|
+
self._hook_manager = hook_manager or HookManager()
|
|
28
|
+
|
|
29
|
+
def execute(
|
|
30
|
+
self,
|
|
31
|
+
method: str,
|
|
32
|
+
url: str,
|
|
33
|
+
**kwargs: Any,
|
|
34
|
+
) -> FrameworkResponse:
|
|
35
|
+
self._logger.info(
|
|
36
|
+
"%s %s",
|
|
37
|
+
method.upper(),
|
|
38
|
+
url,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
self._hook_manager.before_request(
|
|
42
|
+
method=method,
|
|
43
|
+
url=url,
|
|
44
|
+
kwargs=kwargs,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
response = self._session.request(
|
|
48
|
+
method=method,
|
|
49
|
+
url=url,
|
|
50
|
+
**kwargs,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
self._hook_manager.after_response(response)
|
|
54
|
+
|
|
55
|
+
if self._config.auto_raise_exceptions:
|
|
56
|
+
ExceptionMapper.raise_for_response(response)
|
|
57
|
+
|
|
58
|
+
self._logger.info(
|
|
59
|
+
"Status Code: %s | Response Time: %.2f ms",
|
|
60
|
+
response.status_code,
|
|
61
|
+
response.elapsed.total_seconds() * 1000,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
return FrameworkResponse(response)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SessionManager:
|
|
7
|
+
"""
|
|
8
|
+
Creates and manages HTTP sessions for the framework.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(self) -> None:
|
|
12
|
+
self._session = requests.Session()
|
|
13
|
+
|
|
14
|
+
@property
|
|
15
|
+
def session(self) -> requests.Session:
|
|
16
|
+
return self._session
|
|
17
|
+
|
|
18
|
+
def close(self) -> None:
|
|
19
|
+
self._session.close()
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base endpoint definitions.
|
|
3
|
+
|
|
4
|
+
All endpoint classes should inherit from this class.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BaseEndpoints:
|
|
9
|
+
"""
|
|
10
|
+
Base class responsible for building API endpoint paths.
|
|
11
|
+
|
|
12
|
+
Future enhancements:
|
|
13
|
+
- API versioning
|
|
14
|
+
- Service prefixes
|
|
15
|
+
- Tenant support
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
API_VERSION = ""
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def build(cls, path: str) -> str:
|
|
22
|
+
"""
|
|
23
|
+
Returns complete endpoint path.
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
"/users"
|
|
27
|
+
|
|
28
|
+
Future:
|
|
29
|
+
"/api/v1/users"
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
return f"{cls.API_VERSION}{path}"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""
|
|
2
|
+
User endpoint definitions.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pyrestkit.endpoints.base_endpoints import BaseEndpoints
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class UserEndpoints(BaseEndpoints):
|
|
9
|
+
"""
|
|
10
|
+
Contains all endpoints related to Users.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
RESOURCE = "/users"
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def list_users(cls) -> str:
|
|
17
|
+
"""
|
|
18
|
+
GET /users
|
|
19
|
+
"""
|
|
20
|
+
return cls.build(cls.RESOURCE)
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def create_user(cls) -> str:
|
|
24
|
+
"""
|
|
25
|
+
POST /users
|
|
26
|
+
"""
|
|
27
|
+
return cls.build(cls.RESOURCE)
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def get_user(cls, user_id: int) -> str:
|
|
31
|
+
"""
|
|
32
|
+
GET /users/{id}
|
|
33
|
+
"""
|
|
34
|
+
return cls.build(f"{cls.RESOURCE}/{user_id}")
|
|
35
|
+
|
|
36
|
+
@classmethod
|
|
37
|
+
def update_user(cls, user_id: int) -> str:
|
|
38
|
+
"""
|
|
39
|
+
PUT /users/{id}
|
|
40
|
+
"""
|
|
41
|
+
return cls.build(f"{cls.RESOURCE}/{user_id}")
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def delete_user(cls, user_id: int) -> str:
|
|
45
|
+
"""
|
|
46
|
+
DELETE /users/{id}
|
|
47
|
+
"""
|
|
48
|
+
return cls.build(f"{cls.RESOURCE}/{user_id}")
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Custom exceptions used throughout the API automation framework.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .api_exception import APIException
|
|
6
|
+
from .authentication_exception import AuthenticationException
|
|
7
|
+
from .configuration_exception import ConfigurationException
|
|
8
|
+
from .network_exception import NetworkException
|
|
9
|
+
from .response_exception import ResponseException
|
|
10
|
+
from .serialization_exception import SerializationException
|
|
11
|
+
from .validation_exception import ValidationException
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"APIException",
|
|
15
|
+
"AuthenticationException",
|
|
16
|
+
"ConfigurationException",
|
|
17
|
+
"NetworkException",
|
|
18
|
+
"ResponseException",
|
|
19
|
+
"SerializationException",
|
|
20
|
+
"ValidationException",
|
|
21
|
+
]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from pyrestkit.exceptions.api_exception import APIException
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ConfigurationException(APIException):
|
|
5
|
+
"""
|
|
6
|
+
Raised when configuration is invalid.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
def __init__(self, message: str = "Configuration is invalid.") -> None:
|
|
10
|
+
super().__init__(message)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
from pyrestkit.exceptions.authentication_exception import AuthenticationException
|
|
6
|
+
from pyrestkit.exceptions.response_exception import ResponseException
|
|
7
|
+
from pyrestkit.exceptions.validation_exception import ValidationException
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ExceptionMapper:
|
|
11
|
+
"""
|
|
12
|
+
Maps HTTP status codes to framework exceptions.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def raise_for_response(
|
|
17
|
+
response: requests.Response,
|
|
18
|
+
) -> None:
|
|
19
|
+
status = response.status_code
|
|
20
|
+
|
|
21
|
+
if status < 400:
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
if status == 401:
|
|
25
|
+
raise AuthenticationException("Authentication failed.")
|
|
26
|
+
|
|
27
|
+
if status == 422:
|
|
28
|
+
raise ValidationException("Validation failed.")
|
|
29
|
+
|
|
30
|
+
raise ResponseException(
|
|
31
|
+
f"Request failed with status code {status}.",
|
|
32
|
+
)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from pyrestkit.exceptions.api_exception import APIException
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ResponseException(APIException):
|
|
5
|
+
"""
|
|
6
|
+
Raised for unexpected HTTP responses.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
def __init__(self, message: str = "Unexpected response received.") -> None:
|
|
10
|
+
super().__init__(message)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from pyrestkit.exceptions.api_exception import APIException
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SerializationException(APIException):
|
|
5
|
+
"""
|
|
6
|
+
Raised when serialization/deserialization fails.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
def __init__(self, message: str = "Serialization failed.") -> None:
|
|
10
|
+
super().__init__(message)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, cast
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BaseFactory:
|
|
9
|
+
"""
|
|
10
|
+
Base class for loading test data.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
TESTDATA_DIR = Path("testdata")
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def load_json(
|
|
17
|
+
cls,
|
|
18
|
+
filename: str,
|
|
19
|
+
) -> dict[str, Any]:
|
|
20
|
+
file_path = cls.TESTDATA_DIR / filename
|
|
21
|
+
|
|
22
|
+
with file_path.open(
|
|
23
|
+
encoding="utf-8",
|
|
24
|
+
) as file:
|
|
25
|
+
return cast(dict[str, Any], json.load(file))
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
|
|
5
|
+
from pyrestkit.factories.base_factory import BaseFactory
|
|
6
|
+
from pyrestkit.models.request.create_user_request import CreateUserRequest
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class UserFactory(BaseFactory):
|
|
10
|
+
"""
|
|
11
|
+
Factory for CreateUserRequest objects.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
def random(cls) -> CreateUserRequest:
|
|
16
|
+
return CreateUserRequest(
|
|
17
|
+
name=f"User-{random.randint(1000, 9999)}",
|
|
18
|
+
job="QA Engineer",
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def admin(cls) -> CreateUserRequest:
|
|
23
|
+
return CreateUserRequest(
|
|
24
|
+
name="Admin User",
|
|
25
|
+
job="Administrator",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def from_file(
|
|
30
|
+
cls,
|
|
31
|
+
filename: str,
|
|
32
|
+
) -> CreateUserRequest:
|
|
33
|
+
data = cls.load_json(filename)
|
|
34
|
+
|
|
35
|
+
return CreateUserRequest(
|
|
36
|
+
**data,
|
|
37
|
+
)
|
pyrestkit/hooks/hook.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import requests
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Hook:
|
|
9
|
+
"""
|
|
10
|
+
Base class for framework hooks.
|
|
11
|
+
|
|
12
|
+
Override only the callbacks you need.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def before_request(
|
|
16
|
+
self,
|
|
17
|
+
method: str,
|
|
18
|
+
url: str,
|
|
19
|
+
kwargs: dict[str, Any],
|
|
20
|
+
) -> None:
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
def after_response(
|
|
24
|
+
self,
|
|
25
|
+
response: requests.Response,
|
|
26
|
+
) -> None:
|
|
27
|
+
return
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import requests
|
|
6
|
+
|
|
7
|
+
from pyrestkit.hooks.hook import Hook
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class HookManager:
|
|
11
|
+
"""
|
|
12
|
+
Executes registered hooks.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
hooks: list[Hook] | None = None,
|
|
18
|
+
) -> None:
|
|
19
|
+
self._hooks = hooks or []
|
|
20
|
+
|
|
21
|
+
def before_request(
|
|
22
|
+
self,
|
|
23
|
+
method: str,
|
|
24
|
+
url: str,
|
|
25
|
+
kwargs: dict[str, Any],
|
|
26
|
+
) -> None:
|
|
27
|
+
for hook in self._hooks:
|
|
28
|
+
hook.before_request(
|
|
29
|
+
method,
|
|
30
|
+
url,
|
|
31
|
+
kwargs,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def after_response(
|
|
35
|
+
self,
|
|
36
|
+
response: requests.Response,
|
|
37
|
+
) -> None:
|
|
38
|
+
for hook in self._hooks:
|
|
39
|
+
hook.after_response(response)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RequestHook(ABC):
|
|
8
|
+
"""
|
|
9
|
+
Executed before an HTTP request is sent.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def before_request(
|
|
14
|
+
self,
|
|
15
|
+
method: str,
|
|
16
|
+
url: str,
|
|
17
|
+
kwargs: dict[str, Any],
|
|
18
|
+
) -> None: ...
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
|
|
5
|
+
import requests
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ResponseHook(ABC):
|
|
9
|
+
"""
|
|
10
|
+
Executed after an HTTP response is received.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def after_response(
|
|
15
|
+
self,
|
|
16
|
+
response: requests.Response,
|
|
17
|
+
) -> None: ...
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
from pyrestkit.hooks.hook import Hook
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TimingHook(Hook):
|
|
12
|
+
"""
|
|
13
|
+
Measures request execution time.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self) -> None:
|
|
17
|
+
self._start = 0.0
|
|
18
|
+
|
|
19
|
+
def before_request(
|
|
20
|
+
self,
|
|
21
|
+
method: str,
|
|
22
|
+
url: str,
|
|
23
|
+
kwargs: dict[str, Any],
|
|
24
|
+
) -> None:
|
|
25
|
+
self._start = time.perf_counter()
|
|
26
|
+
|
|
27
|
+
def after_response(
|
|
28
|
+
self,
|
|
29
|
+
response: requests.Response,
|
|
30
|
+
) -> None:
|
|
31
|
+
elapsed = (time.perf_counter() - self._start) * 1000
|
|
32
|
+
print(f"Request completed in {elapsed:.2f} ms")
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# @dataclass(slots=True)
|
|
5
|
+
# class CreateUserResponse:
|
|
6
|
+
# id: str
|
|
7
|
+
# name: str
|
|
8
|
+
# job: str
|
|
9
|
+
# created_at: str
|
|
10
|
+
|
|
11
|
+
# @classmethod
|
|
12
|
+
# def from_dict(cls, data: dict) -> "CreateUserResponse":
|
|
13
|
+
# return cls(
|
|
14
|
+
# id=data["id"],
|
|
15
|
+
# name=data["name"],
|
|
16
|
+
# job=data["job"],
|
|
17
|
+
# created_at=data["createdAt"],
|
|
18
|
+
# )
|
|
19
|
+
|
|
20
|
+
from dataclasses import dataclass
|
|
21
|
+
|
|
22
|
+
from pyrestkit.models.base_response import BaseResponse
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(slots=True)
|
|
26
|
+
class CreateUserResponse(BaseResponse): ...
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# @dataclass(slots=True)
|
|
5
|
+
# class GetUserResponse:
|
|
6
|
+
# id: int
|
|
7
|
+
# email: str
|
|
8
|
+
# first_name: str
|
|
9
|
+
# last_name: str
|
|
10
|
+
# avatar: str
|
|
11
|
+
|
|
12
|
+
# @classmethod
|
|
13
|
+
# def from_dict(cls, data: dict) -> "GetUserResponse":
|
|
14
|
+
# return cls(
|
|
15
|
+
# id=data["id"],
|
|
16
|
+
# email=data["email"],
|
|
17
|
+
# first_name=data["first_name"],
|
|
18
|
+
# last_name=data["last_name"],
|
|
19
|
+
# avatar=data["avatar"],
|
|
20
|
+
# )
|
|
21
|
+
|
|
22
|
+
from dataclasses import dataclass
|
|
23
|
+
|
|
24
|
+
from pyrestkit.models.base_response import BaseResponse
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(slots=True)
|
|
28
|
+
class GetUserResponse(BaseResponse): ...
|