def __init__(self, path: str = '', spec: Optional[specs.Specification] = None, app: Optional[Starlette] = None, **kwargs: Any): self._path = path.rstrip('/') self._spec = spec self._app = app or Starlette() self._dispatcher = pjrpc.server.AsyncDispatcher(**kwargs) self._endpoints: Dict[str, pjrpc.server.AsyncDispatcher] = { '': self._dispatcher } self._app.add_route(self._path, async_partial(self._rpc_handle, dispatcher=self._dispatcher), methods=['POST']) if self._spec: self._app.add_route(utils.join_path(self._path, self._spec.path), self._generate_spec, methods=['GET']) if self._spec.ui and self._spec.ui_path: ui_path = utils.join_path(self._path, self._spec.ui_path) self._app.add_route(utils.join_path(ui_path, '/'), self._ui_index_page) self._app.add_route(utils.join_path(ui_path, 'index.html'), self._ui_index_page) self._app.routes.append( routing.Mount(ui_path, app=StaticFiles(directory=str( self._spec.ui.get_static_folder()))), )
def add_endpoint( self, prefix: str, subapp: Optional[aiohttp.web.Application] = None, **kwargs: Any, ) -> pjrpc.server.Dispatcher: """ Adds additional endpoint. :param prefix: endpoint prefix :param subapp: aiohttp subapp the endpoint will be served on :param kwargs: arguments to be passed to the dispatcher :py:class:`pjrpc.server.Dispatcher` :return: dispatcher """ prefix = prefix.rstrip('/') dispatcher = pjrpc.server.AsyncDispatcher(**kwargs) self._endpoints[prefix] = dispatcher if subapp: subapp.router.add_post( '', ft.partial(self._rpc_handle, dispatcher=dispatcher)) self._app.add_subapp(utils.join_path(self._path, prefix), subapp) else: self._app.router.add_post( utils.join_path(self._path, prefix), ft.partial(self._rpc_handle, dispatcher=dispatcher), ) return dispatcher
def __init__(self, path: str = '', spec: Optional[specs.Specification] = None, app: Optional[web.Application] = None, **kwargs: Any): self._path = path.rstrip('/') self._spec = spec self._app = app or web.Application() self._dispatcher = pjrpc.server.AsyncDispatcher(**kwargs) self._endpoints: Dict[str, pjrpc.server.AsyncDispatcher] = { '': self._dispatcher } self._app.router.add_post( self._path, ft.partial(self._rpc_handle, dispatcher=self._dispatcher)) if self._spec: self._app.router.add_get( utils.join_path(self._path, self._spec.path), self._generate_spec) if self._spec.ui and self._spec.ui_path: ui_app = web.Application() ui_app.router.add_get('/', self._ui_index_page) ui_app.router.add_get('/index.html', self._ui_index_page) ui_app.router.add_static('/', self._spec.ui.get_static_folder()) self._app.add_subapp( utils.join_path(self._path, self._spec.ui_path), ui_app)
def __init__(self, path: str = '', spec: Optional[specs.Specification] = None, **kwargs: Any): self._path = path self._spec = spec self._dispatcher = pjrpc.server.Dispatcher(**kwargs) self._urls = [ urls.path(path, self._rpc_handle), ] if self._spec: self._urls.append( urls.path(utils.join_path(self._path, self._spec.path), self._generate_spec)) if self._spec.ui and self._spec.ui_path: path = utils.join_path(self._path, self._spec.ui_path) self._urls.extend(( urls.path(utils.join_path(path, '/'), self._ui_index_page), urls.path(utils.join_path(path, 'index.html'), self._ui_index_page), )) self._urls.extend( static(path, document_root=str( self._spec.ui.get_static_folder())))
def _ui_index_page(self, request: HttpRequest) -> HttpResponse: app_path = request.path.rsplit(self._spec.ui_path, maxsplit=1)[0] spec_full_path = utils.join_path(app_path, self._spec.path) return HttpResponse( self._spec.ui.get_index_page(spec_url=spec_full_path), content_type='text/html')
def _ui_index_page(self) -> flask.Response: app_path = flask.request.path.rsplit(self._spec.ui_path, maxsplit=1)[0] spec_full_path = utils.join_path(app_path, self._spec.path) return current_app.response_class( response=self._spec.ui.get_index_page(spec_url=spec_full_path), content_type='text/html', )
async def _ui_index_page(self, request: Request) -> Response: app_path = request.url.path.rsplit(self._spec.ui_path, maxsplit=1)[0] spec_full_path = utils.join_path(app_path, self._spec.path) return Response( content=self._spec.ui.get_index_page(spec_url=spec_full_path), media_type='text/html', )
def init_app(self, app: Union[flask.Flask, flask.Blueprint]) -> None: """ Initializes flask application with JSON-RPC extension. :param app: flask application instance """ for prefix, dispatcher in self._endpoints.items(): path = utils.join_path(self._path, prefix) blueprint = self._blueprints.get(prefix) (blueprint or app).add_url_rule( path, methods=['POST'], view_func=ft.partial(self._rpc_handle, dispatcher=dispatcher), endpoint=path.replace('/', '_'), ) if blueprint: app.register_blueprint(blueprint) if self._spec: app.add_url_rule( utils.join_path(self._path, self._spec.path), methods=['GET'], view_func=self._generate_spec, ) if self._spec.ui and self._spec.ui_path: path = utils.join_path(self._path, self._spec.ui_path) app.add_url_rule(f'{path}/', methods=['GET'], view_func=self._ui_index_page) app.add_url_rule(f'{path}/index.html', methods=['GET'], view_func=self._ui_index_page) app.add_url_rule(f'{path}/<path:filename>', methods=['GET'], view_func=self._ui_static)
def add_endpoint(self, prefix: str, **kwargs: Any) -> pjrpc.server.Dispatcher: """ Adds additional endpoint. :param prefix: endpoint prefix :param kwargs: arguments to be passed to the dispatcher :py:class:`pjrpc.server.Dispatcher` :return: dispatcher """ dispatcher = pjrpc.server.AsyncDispatcher(**kwargs) self._endpoints[prefix] = dispatcher self._app.add_route( utils.join_path(self._path, prefix), async_partial(self._rpc_handle, dispatcher=self._dispatcher), methods=['POST'], ) return dispatcher
def schema(self, path: str, methods: Iterable[Method] = (), methods_map: Dict[str, Iterable[Method]] = {}) -> dict: methods_list: List[Tuple[str, Method]] = [] methods_list.extend((path, method) for method in methods) methods_list.extend( (utils.join_path(path, prefix), method) for prefix, methods in methods_map.items() for method in methods ) for prefix, method in methods_list: method_meta = utils.get_meta(method.method) annotated_spec = method_meta.get('openapi_spec', {}) specs: List[Dict[str, Any]] = [ dict( params_schema=annotated_spec.get('params_schema', UNSET), result_schema=annotated_spec.get('result_schema', UNSET), errors_schema=annotated_spec.get('errors_schema', UNSET), deprecated=annotated_spec.get('deprecated', UNSET), description=annotated_spec.get('description', UNSET), summary=annotated_spec.get('summary', UNSET), tags=annotated_spec.get('tags', UNSET), examples=annotated_spec.get('examples', UNSET), error_examples=annotated_spec.get('error_examples', UNSET), external_docs=annotated_spec.get('external_docs', UNSET), security=annotated_spec.get('security', UNSET), parameters=annotated_spec.get('parameters', UNSET), ), ] for schema_extractor in self._schema_extractors: specs.append( dict( params_schema=schema_extractor.extract_params_schema(method.method, exclude=[method.context]), result_schema=schema_extractor.extract_result_schema(method.method), errors_schema=schema_extractor.extract_errors_schema( method.method, annotated_spec.get('errors') or [], ), deprecated=schema_extractor.extract_deprecation_status(method.method), description=schema_extractor.extract_description(method.method), summary=schema_extractor.extract_summary(method.method), tags=schema_extractor.extract_tags(method.method), examples=schema_extractor.extract_examples(method.method), error_examples=schema_extractor.extract_error_examples( method.method, annotated_spec.get('errors') or [], ), ), ) method_spec = self._merge_specs(specs) request_schema = self._build_request_schema(method.name, method_spec) response_schema = self._build_response_schema(method_spec) request_examples = { example.summary or f'Example#{i}': ExampleObject( summary=example.summary, description=example.description, value=dict( jsonrpc=example.version, id=1, method=method.name, params=example.params, ), ) for i, example in enumerate(method_spec.get('examples') or []) } response_success_examples = { example.summary or f'Example#{i}': ExampleObject( summary=example.summary, description=example.description, value=dict( jsonrpc=example.version, id=1, result=example.result, ), ) for i, example in enumerate(method_spec.get('examples') or []) } response_error_examples = { example.message or f'Error#{i}': ExampleObject( summary=example.summary, description=example.description, value=dict( jsonrpc='2.0', id=1, error=dict( code=example.code, message=example.message, data=example.data, ), ), ) for i, example in enumerate( utils.unique( [ ErrorExample(code=error.code, message=error.message) for error in method_spec.get('errors') or [] ], method_spec.get('error_examples') or [], key=lambda item: item.code, ), ) } self.paths[f'{prefix}#{method.name}'] = Path( post=Operation( requestBody=RequestBody( description='JSON-RPC Request', content={ JSONRPC_MEDIATYPE: MediaType( schema=request_schema, examples=request_examples or UNSET, ), }, required=True, ), responses={ JSONRPC_HTTP_CODE: Response( description='JSON-RPC Response', content={ JSONRPC_MEDIATYPE: MediaType( schema=response_schema, examples={**response_success_examples, **response_error_examples} or UNSET, ), }, ), }, tags=[tag.name for tag in method_spec.get('tags') or []], summary=method_spec['summary'], description=method_spec['description'], deprecated=method_spec['deprecated'], externalDocs=method_spec.get('external_docs', UNSET), security=method_spec.get('security', UNSET), parameters=method_spec.get('parameters', UNSET), ), ) return drop_unset(dc.asdict(self))