django-jsonrpc-framework 0.2.0__tar.gz → 0.2.1__tar.gz

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.
Files changed (73) hide show
  1. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/PKG-INFO +16 -12
  2. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/README.md +10 -10
  3. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/controller/auth/bearer.py +1 -2
  4. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/controller/openrpc/collectors/_method_collector.py +0 -11
  5. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/core/error.py +15 -1
  6. django_jsonrpc_framework-0.2.1/jsonrpc_framework/integration/prometheus/__init__.py +3 -0
  7. django_jsonrpc_framework-0.2.1/jsonrpc_framework/integration/prometheus/_prometheus.py +229 -0
  8. django_jsonrpc_framework-0.2.1/jsonrpc_framework/integration/sentry.py +125 -0
  9. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/logic/dispatcher.py +0 -15
  10. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/components/_components.py +0 -9
  11. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/tests/_conftest/components_conftest.py +0 -10
  12. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/tests/_conftest/info_conftest.py +0 -7
  13. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/tests/_conftest/method_conftest.py +0 -28
  14. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/tests/test_builder.py +0 -2
  15. django_jsonrpc_framework-0.2.1/jsonrpc_framework/py.typed +0 -0
  16. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/pyproject.toml +4 -4
  17. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/.gitignore +0 -0
  18. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/__init__.py +0 -0
  19. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/controller/__init__.py +0 -0
  20. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/controller/_base.py +0 -0
  21. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/controller/_route.py +0 -0
  22. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/controller/auth/__init__.py +0 -0
  23. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/controller/auth/_base.py +0 -0
  24. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/controller/decor.py +0 -0
  25. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/controller/openrpc/__init__.py +0 -0
  26. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/controller/openrpc/_openrpc.py +0 -0
  27. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/controller/openrpc/collectors/__init__.py +0 -0
  28. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/controller/openrpc/collectors/_main_collector.py +0 -0
  29. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/core/__init__.py +0 -0
  30. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/core/models.py +0 -0
  31. {django_jsonrpc_framework-0.2.0/jsonrpc_framework/openrpc → django_jsonrpc_framework-0.2.1/jsonrpc_framework/integration}/__init__.py +0 -0
  32. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/logic/__init__.py +0 -0
  33. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/logic/responser.py +0 -0
  34. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/logic/validator.py +0 -0
  35. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/management/commands/generate_openrpc.py +0 -0
  36. {django_jsonrpc_framework-0.2.0/jsonrpc_framework/openrpc/builder → django_jsonrpc_framework-0.2.1/jsonrpc_framework/openrpc}/__init__.py +0 -0
  37. {django_jsonrpc_framework-0.2.0/jsonrpc_framework/openrpc/document → django_jsonrpc_framework-0.2.1/jsonrpc_framework/openrpc/builder}/__init__.py +0 -0
  38. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/builder/builder.py +0 -0
  39. {django_jsonrpc_framework-0.2.0/jsonrpc_framework/openrpc/tests → django_jsonrpc_framework-0.2.1/jsonrpc_framework/openrpc/document}/__init__.py +0 -0
  40. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/_base.py +0 -0
  41. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/_openrpc_document.py +0 -0
  42. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/common/__init__.py +0 -0
  43. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/common/_descriptor.py +0 -0
  44. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/common/_error.py +0 -0
  45. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/common/_example.py +0 -0
  46. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/common/_link.py +0 -0
  47. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/common/_link_server.py +0 -0
  48. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/common/_pairing_object.py +0 -0
  49. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/common/_reference.py +0 -0
  50. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/common/_schema.py +0 -0
  51. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/common/_tag.py +0 -0
  52. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/common/_utils.py +0 -0
  53. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/components/__init__.py +0 -0
  54. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/external_docs/__init__.py +0 -0
  55. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/external_docs/_external_docs.py +0 -0
  56. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/info/__init__.py +0 -0
  57. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/info/_info.py +0 -0
  58. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/method/__init__.py +0 -0
  59. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/method/_method.py +0 -0
  60. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/server/__init__.py +0 -0
  61. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/document/server/_server.py +0 -0
  62. /django_jsonrpc_framework-0.2.0/jsonrpc_framework/openrpc/tests/_conftest/builder_conftest.py → /django_jsonrpc_framework-0.2.1/jsonrpc_framework/openrpc/tests/__init__.py +0 -0
  63. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/tests/_conftest/__init__.py +0 -0
  64. /django_jsonrpc_framework-0.2.0/jsonrpc_framework/py.typed → /django_jsonrpc_framework-0.2.1/jsonrpc_framework/openrpc/tests/_conftest/builder_conftest.py +0 -0
  65. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/tests/_conftest/external_doc_conftest.py +0 -0
  66. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/tests/_conftest/server_conftest.py +0 -0
  67. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/tests/conftest.py +0 -0
  68. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/tests/test_components.py +0 -0
  69. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/tests/test_external_doc.py +0 -0
  70. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/tests/test_openrcp_info.py +0 -0
  71. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/tests/test_openrcp_method.py +0 -0
  72. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/openrpc/tests/test_server.py +0 -0
  73. {django_jsonrpc_framework-0.2.0 → django_jsonrpc_framework-0.2.1}/jsonrpc_framework/templates/openrpc_docs.html +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: django-jsonrpc-framework
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Django jsonrpc implementation
5
5
  Classifier: Framework :: Django
6
6
  Classifier: Programming Language :: Python :: 3
@@ -9,11 +9,15 @@ Requires-Dist: django>=6.0.5
9
9
  Requires-Dist: pydantic>=2.13.4
10
10
  Provides-Extra: jwt
11
11
  Requires-Dist: pyjwt>=2.13.0; extra == 'jwt'
12
+ Provides-Extra: prometheus
13
+ Requires-Dist: prometheus-client>=0.21; extra == 'prometheus'
14
+ Provides-Extra: sentry
15
+ Requires-Dist: sentry-sdk>=2.0; extra == 'sentry'
12
16
  Description-Content-Type: text/markdown
13
17
 
14
18
  # Django jsonrpc implementation
15
19
 
16
- ## install
20
+ ## Install
17
21
 
18
22
  ```
19
23
  pip install django-jsonrpc-framework
@@ -21,18 +25,18 @@ pip install django-jsonrpc-framework
21
25
 
22
26
  ## Advantages
23
27
 
24
- - Complete support jsonrpc 2.0 (Request, Notificatin, Batch)
25
- - Auto generation openrpc.json 1.3.2 version
26
- - Auto generation OpenRPC documentation (like swagger)
28
+ - Complete support for JSON-RPC 2.0 (Request, Notification, Batch)
29
+ - Auto-generation of `openrpc.json` (OpenRPC 1.3.2)
30
+ - Auto-generation of OpenRPC documentation (like Swagger)
27
31
  - Async support
28
32
 
29
33
  ## Create methods
30
34
 
31
- We provide several methods creating methods.
35
+ We provide several ways to create methods.
32
36
 
33
37
  - Using `method_` prefix
34
38
  - Using `jsonrpc_method` decorator
35
- - Rename existing func to new name
39
+ - Rename an existing function to a new name
36
40
 
37
41
  ``` python
38
42
  from jsonrpc_framework import BaseController
@@ -49,7 +53,7 @@ class EchoController(BaseController):
49
53
 
50
54
  @jsonrpc_method("echo_see_you")
51
55
  def wrong_name(self, name) -> str:
52
- return "See you fron echo_see_you method"
56
+ return "See you from echo_see_you method"
53
57
  ```
54
58
 
55
59
  ## Adding several controllers to one controller
@@ -76,7 +80,7 @@ route = RouteController(
76
80
  )
77
81
  ```
78
82
 
79
- ## Generation openrpc.json and OpenRpc documentation
83
+ ## Generating openrpc.json and OpenRPC documentation
80
84
 
81
85
  ``` python
82
86
  from jsonrpc_framework.controller.openrpc.collectors import OpenRpcCollector
@@ -94,10 +98,10 @@ urlpatterns = [
94
98
  ]
95
99
  ```
96
100
 
97
- ### Openrpc.json example
101
+ ### OpenRPC.json example
98
102
 
99
103
  ![OpenRPC docs](docs/docs/openrpcjson.png)
100
104
 
101
- ## Openrpc doc example
105
+ ## OpenRPC doc example
102
106
 
103
107
  ![OpenRPC docs](docs/docs/docs.png)
@@ -1,6 +1,6 @@
1
1
  # Django jsonrpc implementation
2
2
 
3
- ## install
3
+ ## Install
4
4
 
5
5
  ```
6
6
  pip install django-jsonrpc-framework
@@ -8,18 +8,18 @@ pip install django-jsonrpc-framework
8
8
 
9
9
  ## Advantages
10
10
 
11
- - Complete support jsonrpc 2.0 (Request, Notificatin, Batch)
12
- - Auto generation openrpc.json 1.3.2 version
13
- - Auto generation OpenRPC documentation (like swagger)
11
+ - Complete support for JSON-RPC 2.0 (Request, Notification, Batch)
12
+ - Auto-generation of `openrpc.json` (OpenRPC 1.3.2)
13
+ - Auto-generation of OpenRPC documentation (like Swagger)
14
14
  - Async support
15
15
 
16
16
  ## Create methods
17
17
 
18
- We provide several methods creating methods.
18
+ We provide several ways to create methods.
19
19
 
20
20
  - Using `method_` prefix
21
21
  - Using `jsonrpc_method` decorator
22
- - Rename existing func to new name
22
+ - Rename an existing function to a new name
23
23
 
24
24
  ``` python
25
25
  from jsonrpc_framework import BaseController
@@ -36,7 +36,7 @@ class EchoController(BaseController):
36
36
 
37
37
  @jsonrpc_method("echo_see_you")
38
38
  def wrong_name(self, name) -> str:
39
- return "See you fron echo_see_you method"
39
+ return "See you from echo_see_you method"
40
40
  ```
41
41
 
42
42
  ## Adding several controllers to one controller
@@ -63,7 +63,7 @@ route = RouteController(
63
63
  )
64
64
  ```
65
65
 
66
- ## Generation openrpc.json and OpenRpc documentation
66
+ ## Generating openrpc.json and OpenRPC documentation
67
67
 
68
68
  ``` python
69
69
  from jsonrpc_framework.controller.openrpc.collectors import OpenRpcCollector
@@ -81,10 +81,10 @@ urlpatterns = [
81
81
  ]
82
82
  ```
83
83
 
84
- ### Openrpc.json example
84
+ ### OpenRPC.json example
85
85
 
86
86
  ![OpenRPC docs](docs/docs/openrpcjson.png)
87
87
 
88
- ## Openrpc doc example
88
+ ## OpenRPC doc example
89
89
 
90
90
  ![OpenRPC docs](docs/docs/docs.png)
@@ -1,6 +1,5 @@
1
- from typing import Generic, TypeVar, Any, ClassVar, Protocol
1
+ from typing import Generic, TypeVar, Protocol
2
2
 
3
- from jwt import algorithms
4
3
  from pydantic import BaseModel, ValidationError
5
4
 
6
5
  from jsonrpc_framework.controller.auth import AuthResult, INVALID_AUTH
@@ -19,17 +19,6 @@ from jsonrpc_framework.openrpc.document.common import (
19
19
  )
20
20
 
21
21
  from jsonrpc_framework.core.error import RpcError
22
- from jsonrpc_framework.openrpc.document.common import (
23
- OpenRcpContentDescriptorObject,
24
- OpenRcpTypeSchema,
25
- OpenRpcDataSchema,
26
- OpenRpcErrorObject,
27
- OpenRpcRefSchema,
28
- OpenRpcTag,
29
- validate_type_name,
30
- )
31
- from jsonrpc_framework.openrpc.document.components import OpenRpcComponents
32
- from jsonrpc_framework.openrpc.document.method._method import OpenRpcMethod
33
22
 
34
23
 
35
24
  class ExampleCollector: ...
@@ -3,6 +3,20 @@ from typing import Any
3
3
  from pydantic import BaseModel
4
4
 
5
5
 
6
+ __all__ = [
7
+ "RpcError",
8
+ "ParseError",
9
+ "InvalidRequestError",
10
+ "MethodNotFoundError",
11
+ "InvalidParamsError",
12
+ "InternalError",
13
+ "ParseExcError",
14
+ "InvalidRequestExcError",
15
+ "UnauthorizedError",
16
+ "ForbiddenError",
17
+ ]
18
+
19
+
6
20
  class RpcError(BaseModel):
7
21
  code: int
8
22
  message: str
@@ -46,7 +60,7 @@ class InvalidRequestExcError(Exception): ...
46
60
 
47
61
  class UnauthorizedError(RpcError):
48
62
  code: int = -32001
49
- message: str = "Unauthorized"
63
+ message: str = "Unauthenticated"
50
64
 
51
65
 
52
66
  class ForbiddenError(RpcError):
@@ -0,0 +1,3 @@
1
+ from ._prometheus import enable_prometheus, reset_prometheus, MetricsView
2
+
3
+ __all__ = ["enable_prometheus", "reset_prometheus", "MetricsView"]
@@ -0,0 +1,229 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+ import time
5
+
6
+ from jsonrpc_framework.logic.dispatcher import RpcDispatcher
7
+ from jsonrpc_framework.core.error import RpcError
8
+ from jsonrpc_framework.core.models import ErrorResponse
9
+ from django.views import View
10
+ from django.http import HttpResponse
11
+
12
+ try:
13
+ from prometheus_client import (
14
+ CONTENT_TYPE_LATEST,
15
+ REGISTRY,
16
+ CollectorRegistry,
17
+ Counter,
18
+ Histogram,
19
+ generate_latest,
20
+ )
21
+ except ImportError:
22
+ raise ImportError(
23
+ "prometheus-client is not installed, please install it with "
24
+ "`pip install django-jsonrpc-framework[prometheus]`"
25
+ ) from None
26
+
27
+
28
+ if TYPE_CHECKING:
29
+ from django.http import HttpRequest
30
+
31
+ from jsonrpc_framework.logic.validator import BatchType, RequestType
32
+ from jsonrpc_framework.core.models import MethodType
33
+ from jsonrpc_framework.logic.dispatcher import (
34
+ BatchResponseType,
35
+ HandlerType,
36
+ ResponseType,
37
+ )
38
+
39
+ __all__ = ["enable_prometheus", "reset_prometheus", "MetricsView"]
40
+
41
+
42
+ _enabled: bool = False
43
+ _registry: CollectorRegistry | None = None
44
+ _original_dispatch_single = None
45
+ _original_dispatch = None
46
+
47
+
48
+ def enable_prometheus(*, registry: CollectorRegistry | None = None) -> None:
49
+ global _enabled, _registry, _original_dispatch_single, _original_dispatch
50
+
51
+ if _enabled:
52
+ return
53
+
54
+ if registry is None:
55
+ registry = REGISTRY
56
+
57
+ _registry = registry
58
+
59
+ requests_total = Counter(
60
+ "jsonrpc_requests_total",
61
+ "Total JSON-RPC requests",
62
+ labelnames=("method", "result"),
63
+ registry=_registry,
64
+ )
65
+ request_duration = Histogram(
66
+ "jsonrpc_request_duration_seconds",
67
+ "JSON-RPC request duration in seconds",
68
+ labelnames=("method",),
69
+ registry=_registry,
70
+ )
71
+ errors_total = Counter(
72
+ "jsonrpc_errors_total",
73
+ "Total JSON-RPC errors",
74
+ labelnames=("method", "code"),
75
+ registry=_registry,
76
+ )
77
+
78
+ _original_dispatch_single = RpcDispatcher._dispatch_single
79
+ _original_dispatch = RpcDispatcher.dispatch
80
+
81
+ _patch_dispatch_single(
82
+ RpcDispatcher,
83
+ requests_total=requests_total,
84
+ request_duration=request_duration,
85
+ errors_total=errors_total,
86
+ )
87
+ _patch_dispatch(
88
+ RpcDispatcher,
89
+ requests_total=requests_total,
90
+ request_duration=request_duration,
91
+ errors_total=errors_total,
92
+ )
93
+ _enabled = True
94
+
95
+
96
+ def reset_prometheus() -> None:
97
+ global _enabled, _registry
98
+ global _original_dispatch_single, _original_dispatch
99
+
100
+ if _original_dispatch_single is not None:
101
+ setattr(RpcDispatcher, "_dispatch_single", _original_dispatch_single)
102
+ if _original_dispatch is not None:
103
+ setattr(RpcDispatcher, "dispatch", _original_dispatch)
104
+
105
+ _original_dispatch_single = None
106
+ _original_dispatch = None
107
+ _registry = None
108
+ _enabled = False
109
+
110
+
111
+ def _patch_dispatch_single(
112
+ dispatcher: type[RpcDispatcher],
113
+ *,
114
+ requests_total: Counter,
115
+ request_duration: Histogram,
116
+ errors_total: Counter,
117
+ ) -> None:
118
+ old = dispatcher._dispatch_single
119
+
120
+ async def patched(
121
+ self: RpcDispatcher,
122
+ request: RequestType,
123
+ registry: dict[MethodType, HandlerType],
124
+ http_request: HttpRequest,
125
+ ) -> ResponseType:
126
+ method = request.method
127
+ start = time.perf_counter()
128
+
129
+ try:
130
+ result = await old(self, request, registry, http_request)
131
+ except Exception:
132
+ duration = time.perf_counter() - start
133
+ request_duration.labels(method=method).observe(duration)
134
+ requests_total.labels(method=method, result="error").inc()
135
+ raise
136
+
137
+ duration = time.perf_counter() - start
138
+
139
+ _observe_outcome(
140
+ method=method,
141
+ duration=duration,
142
+ result=result,
143
+ requests_total=requests_total,
144
+ request_duration=request_duration,
145
+ errors_total=errors_total,
146
+ )
147
+
148
+ return result
149
+
150
+ setattr(dispatcher, "_dispatch_single", patched)
151
+
152
+
153
+ def _patch_dispatch(
154
+ dispatcher: type[RpcDispatcher],
155
+ *,
156
+ requests_total: Counter,
157
+ request_duration: Histogram,
158
+ errors_total: Counter,
159
+ ) -> None:
160
+ old = dispatcher.dispatch
161
+
162
+ async def patched(
163
+ self: RpcDispatcher,
164
+ body: RequestType | BatchType | RpcError,
165
+ registry: dict[MethodType, HandlerType],
166
+ http_request: HttpRequest,
167
+ ) -> ResponseType | BatchResponseType:
168
+ if not isinstance(body, RpcError):
169
+ return await old(self, body, registry, http_request)
170
+
171
+ start = time.perf_counter()
172
+ result = await old(self, body, registry, http_request)
173
+ duration = time.perf_counter() - start
174
+
175
+ _observe_outcome(
176
+ method="_invalid",
177
+ duration=duration,
178
+ result=result,
179
+ requests_total=requests_total,
180
+ request_duration=request_duration,
181
+ errors_total=errors_total,
182
+ )
183
+
184
+ return result
185
+
186
+ setattr(dispatcher, "dispatch", patched)
187
+
188
+
189
+ def _observe_outcome(
190
+ method: str,
191
+ duration: float,
192
+ result: ResponseType | BatchResponseType,
193
+ requests_total: Counter,
194
+ request_duration: Histogram,
195
+ errors_total: Counter,
196
+ ) -> None:
197
+ request_duration.labels(method=method).observe(duration)
198
+
199
+ if result is None:
200
+ result_label = "notification"
201
+ elif isinstance(result, ErrorResponse):
202
+ result_label = "error"
203
+ errors_total.labels(
204
+ method=method,
205
+ code=str(result.error.code),
206
+ ).inc()
207
+ else:
208
+ result_label = "success"
209
+
210
+ requests_total.labels(method=method, result=result_label).inc()
211
+
212
+
213
+ class MetricsView(View):
214
+ http_method_names = ["get"]
215
+
216
+ def get(
217
+ self,
218
+ request: HttpRequest,
219
+ *args: tuple[Any],
220
+ **kwargs: dict[str, Any],
221
+ ) -> HttpResponse:
222
+ return HttpResponse(
223
+ generate_latest(_active_registry()),
224
+ content_type=CONTENT_TYPE_LATEST,
225
+ )
226
+
227
+
228
+ def _active_registry() -> CollectorRegistry:
229
+ return REGISTRY if _registry is None else _registry
@@ -0,0 +1,125 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from typing import Any
5
+ import inspect
6
+
7
+ import sentry_sdk
8
+ from sentry_sdk.integrations import Integration
9
+ from sentry_sdk.utils import (
10
+ capture_internal_exceptions,
11
+ ensure_integration_enabled,
12
+ )
13
+
14
+ from jsonrpc_framework.logic.dispatcher import RpcDispatcher
15
+
16
+ logger = logging.getLogger("django.server")
17
+
18
+
19
+ class JsonRpcIntegration(Integration):
20
+ identifier = "jsonrpc"
21
+ origin = "auto.rpc.jsonrpc"
22
+
23
+ def __init__(
24
+ self,
25
+ *,
26
+ capture_internal_errors: bool = True,
27
+ set_transaction_name: bool = True,
28
+ mechanism_handled: bool = True,
29
+ ) -> None:
30
+ self.capture_internal_errors = capture_internal_errors
31
+ self.set_transaction_name = set_transaction_name
32
+ self.mechanism_handled = mechanism_handled
33
+
34
+ @staticmethod
35
+ def setup_once() -> None:
36
+ _patch_dispatch_single(RpcDispatcher)
37
+ _patch_call_handler(RpcDispatcher)
38
+
39
+
40
+ def _patch_dispatch_single(RpcDispatcher: type) -> None:
41
+ old = getattr(RpcDispatcher, "_dispatch_single")
42
+
43
+ @ensure_integration_enabled(JsonRpcIntegration, old)
44
+ async def patched(
45
+ self: Any,
46
+ request: Any,
47
+ registry: Any,
48
+ http_request: Any,
49
+ ) -> Any:
50
+ integration = sentry_sdk.get_client().get_integration(
51
+ JsonRpcIntegration
52
+ )
53
+ method = request.method
54
+ scope = sentry_sdk.get_current_scope()
55
+
56
+ if integration and integration.set_transaction_name:
57
+ scope.set_transaction_name(
58
+ f"jsonrpc.{method}",
59
+ source="custom",
60
+ )
61
+
62
+ scope.set_tag("jsonrpc.method", method)
63
+
64
+ if request.id is not None:
65
+ scope.set_tag("jsonrpc.id", str(request.id))
66
+
67
+ with sentry_sdk.start_span(
68
+ op="rpc.server",
69
+ name=method,
70
+ origin=JsonRpcIntegration.origin,
71
+ ):
72
+ return await old(self, request, registry, http_request)
73
+
74
+ setattr(RpcDispatcher, "_dispatch_single", patched)
75
+
76
+
77
+ def _patch_call_handler(RpcDispatcher: type) -> None:
78
+ old = getattr(RpcDispatcher, "_call_handler")
79
+
80
+ @ensure_integration_enabled(JsonRpcIntegration, old)
81
+ async def patched(
82
+ self: Any,
83
+ handler: Any,
84
+ bound: Any,
85
+ ) -> Any:
86
+ try:
87
+ return await old(self, handler, bound)
88
+ except Exception:
89
+ raise
90
+
91
+ @ensure_integration_enabled(JsonRpcIntegration, old)
92
+ async def patched_call_handler(
93
+ self: Any,
94
+ handler: Any,
95
+ bound: Any,
96
+ ) -> Any:
97
+ try:
98
+ if bound is None:
99
+ result = handler()
100
+ else:
101
+ result = handler(*bound.args, **bound.kwargs)
102
+ if inspect.isawaitable(result):
103
+ result = await result
104
+ return result
105
+ except Exception as exc:
106
+ integration = sentry_sdk.get_client().get_integration(
107
+ JsonRpcIntegration
108
+ )
109
+ if integration and integration.capture_internal_errors:
110
+ with capture_internal_exceptions():
111
+ sentry_sdk.capture_exception(
112
+ exc,
113
+ hint={
114
+ "mechanism": {
115
+ "type": "jsonrpc",
116
+ "handled": integration.mechanism_handled,
117
+ }
118
+ },
119
+ )
120
+ logger.exception(exc)
121
+ from jsonrpc_framework.core.error import InternalError
122
+
123
+ return InternalError()
124
+
125
+ setattr(RpcDispatcher, "_call_handler", patched_call_handler)
@@ -7,7 +7,6 @@ from typing import Any
7
7
  from django.http import HttpRequest
8
8
 
9
9
  from jsonrpc_framework.core.models import MethodType, ParamType
10
- from jsonrpc_framework.logic.validator import RequestType, BatchType
11
10
  from jsonrpc_framework.core.error import (
12
11
  RpcError,
13
12
  InternalError,
@@ -25,20 +24,6 @@ from jsonrpc_framework.controller.auth import (
25
24
  ANONYMOUS_AUTH,
26
25
  )
27
26
 
28
- from jsonrpc_framework.core.error import (
29
- InternalError,
30
- InvalidParamsError,
31
- MethodNotFoundError,
32
- RpcError,
33
- )
34
- from jsonrpc_framework.core.models import (
35
- ErrorResponse,
36
- MethodType,
37
- Notification,
38
- ParamType,
39
- Request,
40
- SuccessResponse,
41
- )
42
27
  from jsonrpc_framework.logic.validator import BatchType, RequestType
43
28
 
44
29
  type ResponseType = SuccessResponse | ErrorResponse | None
@@ -21,18 +21,9 @@ from jsonrpc_framework.openrpc.document.common._example import (
21
21
  _OpenRpcExampleObjectTD,
22
22
  )
23
23
  from jsonrpc_framework.openrpc.document.common._link import _OpenRpcLinkObjectTD
24
- from jsonrpc_framework.openrpc.document.common._error import (
25
- _OpenRpcErrorObjectTD,
26
- )
27
- from jsonrpc_framework.openrpc.document.common._example import (
28
- _OpenRpcExampleObjectTD,
29
- )
30
24
  from jsonrpc_framework.openrpc.document.common._pairing_object import (
31
25
  _OpenRpcExamplePairingObjectTD,
32
26
  )
33
- from jsonrpc_framework.openrpc.document.common._descriptor import (
34
- _OpenRcpContentDescriptorObjectTD,
35
- )
36
27
  from jsonrpc_framework.openrpc.document.common._tag import _OpenRpcTagTD
37
28
 
38
29
 
@@ -14,16 +14,6 @@ from jsonrpc_framework.openrpc.document.components import OpenRpcComponents
14
14
  from jsonrpc_framework.openrpc.document.components._components import (
15
15
  _OpenRpcComponentsTD,
16
16
  )
17
- from jsonrpc_framework.openrpc.document.common import (
18
- OpenRpcLinkObject,
19
- OpenRpcErrorObject,
20
- OpenRpcExamplePairingObject,
21
- OpenRcpContentDescriptorObject,
22
- OpenRpcTag,
23
- OpenRpcDataSchema,
24
- OpenRpcExampleObject,
25
- )
26
- from jsonrpc_framework.openrpc.document.common import OpenRcpTypeSchema
27
17
 
28
18
 
29
19
  @pytest.fixture
@@ -7,13 +7,6 @@ from jsonrpc_framework.openrpc.document.info._info import _OpenRpcInfoTD
7
7
 
8
8
  import pytest
9
9
 
10
- from jsonrpc_framework.openrpc.document.info import (
11
- OpenRpcContact,
12
- OpenRpcInfo,
13
- OpenRpcLicense,
14
- )
15
- from jsonrpc_framework.openrpc.document.info._info import _OpenRpcInfoTD
16
-
17
10
 
18
11
  @pytest.fixture
19
12
  def openrpc_info() -> OpenRpcInfo:
@@ -22,38 +22,10 @@ from jsonrpc_framework.openrpc.document.info import (
22
22
  OpenRpcContact,
23
23
  OpenRpcLicense,
24
24
  )
25
- from jsonrpc_framework.openrpc.document.method._method import (
26
- OpenRpcMethod,
27
- _OpenRpcMethodTD,
28
- )
29
- from jsonrpc_framework.openrpc.document.server import (
30
- OpenRpcServer,
31
- OpenRpcServerObjectVariable,
32
- )
33
25
 
34
26
  from jsonrpc_framework.openrpc.builder.builder import OpenRpcBuilder
35
27
  from jsonrpc_framework.openrpc.document.common import (
36
- OpenRcpContentDescriptorObject,
37
28
  OpenRcpTypeSchema,
38
- OpenRpcErrorObject,
39
- OpenRpcExampleObject,
40
- OpenRpcExamplePairingObject,
41
- OpenRpcLinkObject,
42
- OpenRpcLinkObjectServer,
43
- OpenRpcTag,
44
- )
45
- from jsonrpc_framework.openrpc.document.external_docs import OpenRpcExternalDoc
46
- from jsonrpc_framework.openrpc.document.info import (
47
- OpenRpcContact,
48
- OpenRpcLicense,
49
- )
50
- from jsonrpc_framework.openrpc.document.method._method import (
51
- OpenRpcMethod,
52
- _OpenRpcMethodTD,
53
- )
54
- from jsonrpc_framework.openrpc.document.server import (
55
- OpenRpcServer,
56
- OpenRpcServerObjectVariable,
57
29
  )
58
30
 
59
31
 
@@ -13,9 +13,7 @@ from jsonrpc_framework.openrpc.document.components._components import (
13
13
  from jsonrpc_framework.openrpc.document.external_docs import OpenRpcExternalDoc
14
14
  from jsonrpc_framework.openrpc.document.info._info import _OpenRpcInfoTD
15
15
  from jsonrpc_framework.openrpc.document.method import OpenRpcMethod
16
- from jsonrpc_framework.openrpc.document.method._method import _OpenRpcMethodTD
17
16
  from jsonrpc_framework.openrpc.document.server import OpenRpcServer
18
- from jsonrpc_framework.openrpc.document.server._server import _OpenRpcServerTD
19
17
 
20
18
 
21
19
  def test_builder(
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "django-jsonrpc-framework"
3
- version = "0.2.0"
3
+ version = "0.2.1"
4
4
  description = "Django jsonrpc implementation"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -16,9 +16,9 @@ dependencies = [
16
16
  ]
17
17
 
18
18
  [project.optional-dependencies]
19
- jwt = [
20
- "pyjwt>=2.13.0",
21
- ]
19
+ jwt = ["pyjwt>=2.13.0"]
20
+ sentry = ["sentry-sdk>=2.0"]
21
+ prometheus = ["prometheus-client>=0.21"]
22
22
 
23
23
  [dependency-groups]
24
24
  dev = [