django-jsonrpc-framework 0.1.0__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 (66) hide show
  1. django_jsonrpc_framework-0.1.0/.gitignore +10 -0
  2. django_jsonrpc_framework-0.1.0/PKG-INFO +101 -0
  3. django_jsonrpc_framework-0.1.0/README.md +90 -0
  4. django_jsonrpc_framework-0.1.0/jsonrpc_framework/__init__.py +8 -0
  5. django_jsonrpc_framework-0.1.0/jsonrpc_framework/controller/__init__.py +7 -0
  6. django_jsonrpc_framework-0.1.0/jsonrpc_framework/controller/_base.py +103 -0
  7. django_jsonrpc_framework-0.1.0/jsonrpc_framework/controller/_route.py +71 -0
  8. django_jsonrpc_framework-0.1.0/jsonrpc_framework/controller/decor.py +81 -0
  9. django_jsonrpc_framework-0.1.0/jsonrpc_framework/controller/openrpc/__init__.py +0 -0
  10. django_jsonrpc_framework-0.1.0/jsonrpc_framework/controller/openrpc/_openrpc.py +101 -0
  11. django_jsonrpc_framework-0.1.0/jsonrpc_framework/controller/openrpc/collectors/__init__.py +5 -0
  12. django_jsonrpc_framework-0.1.0/jsonrpc_framework/controller/openrpc/collectors/_main_collector.py +57 -0
  13. django_jsonrpc_framework-0.1.0/jsonrpc_framework/controller/openrpc/collectors/_method_collector.py +198 -0
  14. django_jsonrpc_framework-0.1.0/jsonrpc_framework/core/__init__.py +3 -0
  15. django_jsonrpc_framework-0.1.0/jsonrpc_framework/core/error.py +37 -0
  16. django_jsonrpc_framework-0.1.0/jsonrpc_framework/core/models.py +41 -0
  17. django_jsonrpc_framework-0.1.0/jsonrpc_framework/logic/__init__.py +9 -0
  18. django_jsonrpc_framework-0.1.0/jsonrpc_framework/logic/dispatcher.py +140 -0
  19. django_jsonrpc_framework-0.1.0/jsonrpc_framework/logic/responser.py +27 -0
  20. django_jsonrpc_framework-0.1.0/jsonrpc_framework/logic/validator.py +56 -0
  21. django_jsonrpc_framework-0.1.0/jsonrpc_framework/management/commands/generate_openrpc.py +49 -0
  22. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/__init__.py +0 -0
  23. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/builder/__init__.py +0 -0
  24. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/builder/builder.py +76 -0
  25. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/__init__.py +0 -0
  26. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/_base.py +8 -0
  27. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/_openrpc_document.py +71 -0
  28. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/common/__init__.py +27 -0
  29. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/common/_descriptor.py +68 -0
  30. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/common/_error.py +41 -0
  31. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/common/_example.py +55 -0
  32. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/common/_link.py +60 -0
  33. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/common/_link_server.py +75 -0
  34. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/common/_pairing_object.py +52 -0
  35. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/common/_reference.py +26 -0
  36. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/common/_schema.py +55 -0
  37. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/common/_tag.py +45 -0
  38. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/common/_utils.py +15 -0
  39. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/components/__init__.py +5 -0
  40. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/components/_components.py +87 -0
  41. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/external_docs/__init__.py +5 -0
  42. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/external_docs/_external_docs.py +27 -0
  43. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/info/__init__.py +7 -0
  44. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/info/_info.py +110 -0
  45. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/method/__init__.py +5 -0
  46. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/method/_method.py +134 -0
  47. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/server/__init__.py +6 -0
  48. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/document/server/_server.py +83 -0
  49. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/tests/__init__.py +0 -0
  50. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/tests/_conftest/__init__.py +6 -0
  51. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/tests/_conftest/builder_conftest.py +0 -0
  52. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/tests/_conftest/components_conftest.py +113 -0
  53. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/tests/_conftest/external_doc_conftest.py +30 -0
  54. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/tests/_conftest/info_conftest.py +59 -0
  55. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/tests/_conftest/method_conftest.py +261 -0
  56. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/tests/_conftest/server_conftest.py +49 -0
  57. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/tests/conftest.py +1 -0
  58. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/tests/test_builder.py +40 -0
  59. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/tests/test_components.py +11 -0
  60. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/tests/test_external_doc.py +24 -0
  61. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/tests/test_openrcp_info.py +24 -0
  62. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/tests/test_openrcp_method.py +26 -0
  63. django_jsonrpc_framework-0.1.0/jsonrpc_framework/openrpc/tests/test_server.py +24 -0
  64. django_jsonrpc_framework-0.1.0/jsonrpc_framework/py.typed +0 -0
  65. django_jsonrpc_framework-0.1.0/jsonrpc_framework/templates/openrpc_docs.html +91 -0
  66. django_jsonrpc_framework-0.1.0/pyproject.toml +62 -0
@@ -0,0 +1,10 @@
1
+ .venv
2
+ .pytest_cache
3
+ .ruff_cache
4
+ .mypy_cache
5
+ __pycache__
6
+ */__pycache__/*
7
+ .cursor
8
+ dist/
9
+ build/
10
+ *.egg-info/
@@ -0,0 +1,101 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-jsonrpc-framework
3
+ Version: 0.1.0
4
+ Summary: Django jsonrpc implementation
5
+ Classifier: Framework :: Django
6
+ Classifier: Programming Language :: Python :: 3
7
+ Requires-Python: >=3.13
8
+ Requires-Dist: django>=6.0.5
9
+ Requires-Dist: pydantic>=2.13.4
10
+ Description-Content-Type: text/markdown
11
+
12
+ # Django jsonrpc implementation
13
+
14
+ ## Advantages
15
+
16
+ - Complete support jsonrpc 2.0 (Request, Notificatin, Batch)
17
+ - Auto generation openrpc.json 1.3.2 version
18
+ - Auto generation OpenRPC documentation (like swagger)
19
+ - Async support
20
+
21
+ ## Create methods
22
+
23
+ We provide several methods creating methods.
24
+
25
+ - Using `method_` prefix
26
+ - Using `jsonrpc_method` decorator
27
+ - Rename existing func to new name
28
+
29
+ ``` python
30
+
31
+ from jsonrpc_framework import BaseController
32
+ from jsonrpc_framework.controller.decor import jsonrpc_method
33
+
34
+ class EchoController(BaseController):
35
+
36
+ def method_echo_hello(self, name: str) -> str:
37
+ return f"hello {name}"
38
+
39
+ @jsonrpc_method
40
+ def echo_goodbye(self, name: str) -> str:
41
+ return f"goodbye {name}"
42
+
43
+ @jsonrpc_method("echo_see_you")
44
+ def wrong_name(self, name) -> str:
45
+ return "See you fron echo_see_you method"
46
+
47
+ ```
48
+
49
+ ## Adding several controllers to one controller
50
+
51
+ ``` python
52
+ from jsonrpc_framework import RouteController
53
+
54
+ class PrintController(BaseController):
55
+
56
+ async def method_print_hello(self, name) -> None:
57
+ print(f"hello {name}")
58
+
59
+ @jsonrpc_method
60
+ async def print_goodbye(self, name) -> None:
61
+ print(f"goodbye {name}")
62
+
63
+
64
+ route = RouteController(
65
+ 'jsonrpc',
66
+ controllers=[
67
+ PrintController,
68
+ EchoController,
69
+ ]
70
+
71
+ )
72
+ ```
73
+
74
+ ## Generation openrpc.json and OpenRpc documentation
75
+
76
+ ``` python
77
+ from jsonrpc_framework.controller.openrpc.collectors import OpenRpcCollector
78
+
79
+ collector = OpenRpcCollector(
80
+ PrintController,
81
+ EchoController,
82
+ title='My mini API'
83
+ )
84
+
85
+
86
+ urlpatterns = [
87
+ path('echorpc', EchoController,as_view),
88
+ path('jsonrpc', route.as_view()),
89
+ path('openrpc.json', OpenRpcJsonView.as_view(collector=collector)),
90
+ path('docs', OpenRpcDocView.as_view()),
91
+ ]
92
+
93
+ ```
94
+
95
+ ### Openrpc.json example
96
+
97
+ ![OpenRPC docs](docs/docs/openrpcjson.png)
98
+
99
+ ## Openrpc doc example
100
+
101
+ ![OpenRPC docs](docs/docs/docs.png)
@@ -0,0 +1,90 @@
1
+ # Django jsonrpc implementation
2
+
3
+ ## Advantages
4
+
5
+ - Complete support jsonrpc 2.0 (Request, Notificatin, Batch)
6
+ - Auto generation openrpc.json 1.3.2 version
7
+ - Auto generation OpenRPC documentation (like swagger)
8
+ - Async support
9
+
10
+ ## Create methods
11
+
12
+ We provide several methods creating methods.
13
+
14
+ - Using `method_` prefix
15
+ - Using `jsonrpc_method` decorator
16
+ - Rename existing func to new name
17
+
18
+ ``` python
19
+
20
+ from jsonrpc_framework import BaseController
21
+ from jsonrpc_framework.controller.decor import jsonrpc_method
22
+
23
+ class EchoController(BaseController):
24
+
25
+ def method_echo_hello(self, name: str) -> str:
26
+ return f"hello {name}"
27
+
28
+ @jsonrpc_method
29
+ def echo_goodbye(self, name: str) -> str:
30
+ return f"goodbye {name}"
31
+
32
+ @jsonrpc_method("echo_see_you")
33
+ def wrong_name(self, name) -> str:
34
+ return "See you fron echo_see_you method"
35
+
36
+ ```
37
+
38
+ ## Adding several controllers to one controller
39
+
40
+ ``` python
41
+ from jsonrpc_framework import RouteController
42
+
43
+ class PrintController(BaseController):
44
+
45
+ async def method_print_hello(self, name) -> None:
46
+ print(f"hello {name}")
47
+
48
+ @jsonrpc_method
49
+ async def print_goodbye(self, name) -> None:
50
+ print(f"goodbye {name}")
51
+
52
+
53
+ route = RouteController(
54
+ 'jsonrpc',
55
+ controllers=[
56
+ PrintController,
57
+ EchoController,
58
+ ]
59
+
60
+ )
61
+ ```
62
+
63
+ ## Generation openrpc.json and OpenRpc documentation
64
+
65
+ ``` python
66
+ from jsonrpc_framework.controller.openrpc.collectors import OpenRpcCollector
67
+
68
+ collector = OpenRpcCollector(
69
+ PrintController,
70
+ EchoController,
71
+ title='My mini API'
72
+ )
73
+
74
+
75
+ urlpatterns = [
76
+ path('echorpc', EchoController,as_view),
77
+ path('jsonrpc', route.as_view()),
78
+ path('openrpc.json', OpenRpcJsonView.as_view(collector=collector)),
79
+ path('docs', OpenRpcDocView.as_view()),
80
+ ]
81
+
82
+ ```
83
+
84
+ ### Openrpc.json example
85
+
86
+ ![OpenRPC docs](docs/docs/openrpcjson.png)
87
+
88
+ ## Openrpc doc example
89
+
90
+ ![OpenRPC docs](docs/docs/docs.png)
@@ -0,0 +1,8 @@
1
+ from .controller import BaseController, RouteController
2
+ from .controller.decor import jsonrpc_method
3
+
4
+ __all__ = [
5
+ "BaseController",
6
+ "RouteController",
7
+ "jsonrpc_method",
8
+ ]
@@ -0,0 +1,7 @@
1
+ from ._base import BaseController
2
+ from ._route import RouteController
3
+
4
+ __all__ = [
5
+ "BaseController",
6
+ "RouteController",
7
+ ]
@@ -0,0 +1,103 @@
1
+ import logging
2
+ from typing import Any
3
+ from collections.abc import Callable
4
+
5
+ from django.views import View
6
+ from django.http import HttpRequest, HttpResponse
7
+
8
+ from jsonrpc_framework.logic.dispatcher import RpcDispatcher
9
+ from jsonrpc_framework.logic.validator import RequestValidator, RequestType, BatchType
10
+ from jsonrpc_framework.logic.responser import ResponseBuilder
11
+
12
+ from jsonrpc_framework.core.error import RpcError
13
+ from jsonrpc_framework.core.models import MethodType
14
+
15
+
16
+
17
+ logger = logging.getLogger("django.server")
18
+
19
+
20
+ class BaseController(View):
21
+ http_method_names = ["post"]
22
+ path: str = "jsonrpc"
23
+
24
+ registry: dict[MethodType, Callable[..., Any]]
25
+
26
+ dispatcher: RpcDispatcher
27
+ validator: RequestValidator
28
+ response_builder: ResponseBuilder
29
+
30
+
31
+ def __init__(self, *args: tuple[Any], **kwargs: dict[str, Any]):
32
+ super().__init__(*args, **kwargs)
33
+
34
+ self.registry = self._collect_declared_methods()
35
+
36
+ self.dispatcher = RpcDispatcher()
37
+ self.validator = RequestValidator()
38
+ self.response_builder = ResponseBuilder()
39
+
40
+
41
+ def _collect_declared_methods(self) -> dict[MethodType, Callable[..., Any]]:
42
+ registry: dict[MethodType, Callable[..., Any]] = {}
43
+
44
+ for name, value in vars(self.__class__).items():
45
+ if not callable(value):
46
+ continue
47
+
48
+ rpc_name = getattr(value, "__rpc_method_name__", None)
49
+
50
+ if rpc_name is not None:
51
+ method_name = rpc_name
52
+ elif name.startswith("method_"):
53
+ method_name = name.replace("method_", "")
54
+ else:
55
+ continue
56
+
57
+ if method_name in registry:
58
+ raise ValueError(f"Method {method_name} already registered in {self.__class__.__name__}")
59
+
60
+ registry[method_name] = getattr(self, name)
61
+
62
+ return registry
63
+
64
+ async def post(
65
+ self,
66
+ request: HttpRequest,
67
+ *args: tuple[Any],
68
+ **kwargs: dict[str, Any],
69
+ ) -> HttpResponse:
70
+ body = self.validator.validate_body(request.body)
71
+
72
+ result = await self.dispatcher.dispatch(body, registry=self.registry)
73
+ self._log_jsonrpc_methods(request, body)
74
+
75
+ return self.response_builder.build_response(result)
76
+
77
+ def _log_jsonrpc_methods(
78
+ self,
79
+ request: HttpRequest,
80
+ body: RequestType | BatchType | RpcError,
81
+ ) -> None:
82
+ methods = self._extract_method_names(body)
83
+
84
+ if methods:
85
+ logger.info(f"JSONRPC {request.path} {methods}")
86
+
87
+ def _extract_method_names(
88
+ self,
89
+ body: RequestType | BatchType | RpcError,
90
+ ) -> list[str] | str:
91
+ if isinstance(body, RpcError):
92
+ return []
93
+
94
+ if isinstance(body, list):
95
+ methods: list[str] = []
96
+
97
+ for item in body:
98
+ if isinstance(item, RpcError):
99
+ continue
100
+ methods.append(item.method)
101
+ return methods
102
+
103
+ return body.method
@@ -0,0 +1,71 @@
1
+ from collections import defaultdict
2
+ from collections.abc import Callable
3
+ from typing import Any
4
+
5
+
6
+ from jsonrpc_framework.controller._base import BaseController
7
+
8
+ class RouteController(BaseController):
9
+ """The conroller that collects BaseControllers to merge
10
+ their methods into a single controller.
11
+
12
+ Args:
13
+ path: The path to use in urlconfig.
14
+ controllers: The list of BaseControllers to merge.
15
+ """
16
+ controllers: list[type[BaseController] | BaseController] = []
17
+
18
+ def __init__(
19
+ self,
20
+ path: str,
21
+ controllers: list[type[BaseController] | BaseController],
22
+ ):
23
+ super().__init__()
24
+ self.path = path
25
+ self.controllers = controllers
26
+
27
+ merged_registry: dict[str, Callable[..., Any]] = {}
28
+ key_sources: dict[str, list[str]] = defaultdict(list)
29
+
30
+ for raw_controller in controllers:
31
+
32
+ controller = (
33
+ raw_controller()
34
+ if isinstance(raw_controller, type)
35
+ else raw_controller
36
+ )
37
+
38
+ if not isinstance(controller, BaseController):
39
+ raise TypeError(
40
+ "Each controller must be a BaseController instance or subclass"
41
+ )
42
+
43
+ controller_name = controller.__class__.__name__
44
+ for method_name, callback in controller.registry.items():
45
+ key_sources[method_name].append(controller_name)
46
+ if method_name in merged_registry:
47
+ continue
48
+ merged_registry[method_name] = callback
49
+
50
+ conflicts = {
51
+ method_name: names
52
+ for method_name, names in key_sources.items()
53
+ if len(names) > 1
54
+ }
55
+ if conflicts:
56
+ details = "; ".join(
57
+ f"{method!r} -> {sources}"
58
+ for method, sources in sorted(conflicts.items())
59
+ )
60
+ raise ValueError(f"Duplicate JSON-RPC methods detected: {details}")
61
+
62
+ self.registry = merged_registry
63
+
64
+
65
+ def as_view(self, **initkwargs: Any) -> Any: # type: ignore[override]
66
+ return BaseController.as_view.__func__( # type: ignore[attr-defined]
67
+ self.__class__,
68
+ path=self.path,
69
+ controllers=self.controllers,
70
+ **initkwargs,
71
+ )
@@ -0,0 +1,81 @@
1
+ from collections.abc import Callable
2
+ from functools import wraps
3
+ from typing import Any
4
+
5
+
6
+ def _decorate[R, **P](
7
+ func: Callable[P, R],
8
+ rpc_name: str,
9
+ summary: str | None = None,
10
+ description: str | None = None,
11
+ tags: list[str] | None = None,
12
+ ) -> Callable[P, R]:
13
+ # Keep an explicit RPC alias on the callable, because class attribute
14
+ # names are used during registry collection and cannot be renamed here.
15
+ setattr(func, "__rpc_method_name__", rpc_name)
16
+ setattr(func, "__rpc_method_summary__", summary)
17
+ setattr(func, "__rpc_method_description__", description)
18
+ setattr(func, "__rpc_method_tags__", tags)
19
+
20
+ @wraps(func)
21
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
22
+ return func(*args, **kwargs)
23
+
24
+ setattr(wrapper, "__rpc_method_name__", rpc_name)
25
+ setattr(wrapper, "__rpc_method_summary__", summary)
26
+ setattr(wrapper, "__rpc_method_description__", description)
27
+ setattr(wrapper, "__rpc_method_tags__", tags)
28
+
29
+ return wrapper
30
+
31
+
32
+ def simple_decorator[R, **P](
33
+ func: Callable[P, R],
34
+ ) -> Callable[P, R]:
35
+ return _decorate(
36
+ func,
37
+ rpc_name=func.__name__,
38
+ description=func.__doc__,
39
+ )
40
+
41
+
42
+ def parametrized_decorator[R, **P](
43
+ func: Callable[P, R],
44
+ *,
45
+ name: str | None = None,
46
+ summary: str | None = None,
47
+ description: str | None = None,
48
+ tags: list[str] | None = None,
49
+ ) -> Callable[P, R]:
50
+ rpc_name = name if isinstance(name, str) else func.__name__
51
+ return _decorate(
52
+ func,
53
+ rpc_name,
54
+ summary,
55
+ description,
56
+ tags,
57
+ )
58
+
59
+
60
+ def jsonrpc_method(
61
+ name_or_func: str | Callable[..., Any] | None = None,
62
+ *,
63
+ summary: str | None = None,
64
+ description: str | None = None,
65
+ tags: list[str] | None = None,
66
+ ) -> Callable[..., Any]:
67
+ def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
68
+ return parametrized_decorator(
69
+ func,
70
+ name=name_or_func if isinstance(name_or_func, str) else None,
71
+ summary=summary,
72
+ description=description,
73
+ tags=tags,
74
+ )
75
+
76
+ if callable(name_or_func):
77
+ return simple_decorator(
78
+ name_or_func,
79
+ )
80
+
81
+ return decorator
@@ -0,0 +1,101 @@
1
+ from pathlib import Path
2
+ from typing import Callable, override, Any
3
+
4
+ from django.views.generic.base import TemplateView
5
+ from django.conf import settings
6
+ from django.http import HttpRequest, HttpResponse, HttpResponseBase
7
+ from django.http.response import FileResponse
8
+
9
+ from django.views import View
10
+
11
+ from jsonrpc_framework.controller.openrpc.collectors import OpenRpcCollector
12
+
13
+ class OpenRpcJsonView(View):
14
+
15
+ http_method_names = ["get"]
16
+ path = "openrpc.json"
17
+ collector: OpenRpcCollector = OpenRpcCollector()
18
+
19
+ def __init__(
20
+ self,
21
+ *args: tuple[Any],
22
+ **kwargs: dict[str, Any]
23
+ ):
24
+ super().__init__(*args, **kwargs)
25
+
26
+ def get(self, request: HttpRequest, *args: tuple[Any], **kwargs: dict[str, Any]) -> HttpResponse | FileResponse:
27
+ file_path = getattr(
28
+ settings, "DJANGO_JSONRPC_DOCS", {}
29
+ ).get("FILE_PATH", None)
30
+
31
+ if file_path is not None and Path(file_path).exists():
32
+ return FileResponse(
33
+ Path(file_path).open("rb"),
34
+ filename=Path(file_path).name,
35
+ )
36
+
37
+ if not self.collector.is_collected:
38
+ self.collector.collect()
39
+
40
+ document = self.collector.build_document()
41
+
42
+ return HttpResponse(
43
+ content=document,
44
+ content_type="application/json; charset=utf-8",
45
+ )
46
+
47
+ @classmethod
48
+ @override
49
+ def as_view(
50
+ cls,
51
+ *,
52
+ collector: OpenRpcCollector | None = None,
53
+ **initkwargs: Any,
54
+ ) -> Callable[..., HttpResponseBase]:
55
+ return super().as_view(collector=collector, **initkwargs)
56
+
57
+
58
+
59
+ class OpenRpcDocView(TemplateView):
60
+ template_name = "openrpc_docs.html"
61
+
62
+ def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
63
+ ctx = super().get_context_data(**kwargs)
64
+
65
+ docs_cfg = getattr(settings, "DJANGO_JSONRPC_DOCS", {})
66
+
67
+ schema_path = docs_cfg.get("SCHEMA_PATH", "/openrpc.json")
68
+ schema_url = self.request.build_absolute_uri(schema_path)
69
+
70
+ ctx.update(
71
+ {
72
+ "openrpc_schema_url": schema_url,
73
+ "page_title": docs_cfg.get("TITLE", "OpenRPC Docs"),
74
+ "docs_react_css_url": docs_cfg.get(
75
+ "DOCS_REACT_CSS_URL",
76
+ "https://cdn.jsdelivr.net/npm/@open-rpc/docs-react@2.1.1/dist/docs-react.css",
77
+ ),
78
+ "esm_react_url": docs_cfg.get(
79
+ "ESM_REACT_URL",
80
+ "https://esm.sh/react@18.3.1",
81
+ ),
82
+ "esm_react_dom_url": docs_cfg.get(
83
+ "ESM_REACT_DOM_URL",
84
+ "https://esm.sh/react-dom@18.3.1/client?deps=react@18.3.1",
85
+ ),
86
+ "esm_openrpc_docs_url": docs_cfg.get(
87
+ "ESM_OPENRPC_DOCS_URL",
88
+ "https://esm.sh/@open-rpc/docs-react@2.1.1?deps=react@18.3.1,react-dom@18.3.1",
89
+ ),
90
+ "esm_mui_styles_url": docs_cfg.get(
91
+ "ESM_MUI_STYLES_URL",
92
+ "https://esm.sh/@mui/material@6.3.1/styles?deps=react@18.3.1,react-dom@18.3.1",
93
+ ),
94
+ "esm_mui_css_baseline_url": docs_cfg.get(
95
+ "ESM_MUI_CSS_BASELINE_URL",
96
+ "https://esm.sh/@mui/material@6.3.1/CssBaseline?deps=react@18.3.1,react-dom@18.3.1",
97
+ ),
98
+ "openrpc_theme_mode": docs_cfg.get("THEME_MODE", "dark"),
99
+ }
100
+ )
101
+ return ctx
@@ -0,0 +1,5 @@
1
+ from ._main_collector import OpenRpcCollector
2
+
3
+ __all__ = [
4
+ "OpenRpcCollector",
5
+ ]
@@ -0,0 +1,57 @@
1
+
2
+ from jsonrpc_framework.controller._base import BaseController
3
+ from jsonrpc_framework.openrpc.builder.builder import OpenRpcBuilder
4
+ from jsonrpc_framework.openrpc.document.info import OpenRpcContact, OpenRpcLicense
5
+
6
+ from jsonrpc_framework.controller.openrpc.collectors._method_collector import MethodsCollector
7
+
8
+ type ControllerType = type[BaseController] | BaseController
9
+
10
+ class OpenRpcCollector:
11
+
12
+ controllers: tuple[type[BaseController] | BaseController, ...]
13
+ builder: OpenRpcBuilder
14
+ is_collected: bool
15
+
16
+ method_collector: MethodsCollector
17
+
18
+ def __init__(
19
+ self,
20
+ *controllers: ControllerType,
21
+ title: str = "Django-jsonrpc API",
22
+ version: str = "1.0.0",
23
+ description: str | None = None,
24
+ terms_of_service: str | None = None,
25
+ contact: OpenRpcContact | None = None,
26
+ license: OpenRpcLicense | None = None,
27
+ ):
28
+ self.controllers = controllers
29
+
30
+ self.method_collector = MethodsCollector()
31
+ self.builder = OpenRpcBuilder(
32
+ title=title,
33
+ version=version,
34
+ description=description,
35
+ terms_of_service=terms_of_service,
36
+ contact=contact,
37
+ license=license,
38
+ )
39
+ self.is_collected = False
40
+
41
+ def collect(self) -> None:
42
+ for controller in self.controllers:
43
+ if isinstance(controller, type):
44
+ controller = controller()
45
+
46
+ self.method_collector.collect(controller)
47
+
48
+ for method in self.method_collector.methods:
49
+ self.builder.add_method(method)
50
+
51
+ self.builder.add_components(self.method_collector.components)
52
+ self.is_collected = True
53
+
54
+
55
+ def build_document(self) -> str:
56
+
57
+ return self.builder.build_json()