From 184d3f1fa093af6a38194c64a4debef0254944f1 Mon Sep 17 00:00:00 2001 From: Luo Lu Date: Wed, 20 Apr 2022 04:30:39 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=F0=9F=90=9B=20create=20API=20schema?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/v1/views/app.py | 16 ++--- arkid/core/extension/app_protocol.py | 58 ++++++++++++------- arkid/redoc/view.py | 18 +++++- arkid/urls.py | 3 +- .../com_longgui_oauth2_server/__init__.py | 2 + templates/swagger.html | 27 +++++++++ 6 files changed, 94 insertions(+), 30 deletions(-) create mode 100644 templates/swagger.html diff --git a/api/v1/views/app.py b/api/v1/views/app.py index 13f834fe2..a6a0d2342 100644 --- a/api/v1/views/app.py +++ b/api/v1/views/app.py @@ -1,4 +1,5 @@ from ninja import Schema +from typing import Union, Literal from pydantic import Field from arkid.core.api import api from arkid.core.models import App @@ -6,20 +7,21 @@ from arkid.core.translation import gettext_default as _ from arkid.extension.models import TenantExtensionConfig from arkid.core.event import Event, register_event, dispatch_event -from arkid.core.extension.app_protocol import create_app_protocol_extension_config_schema +from arkid.core.extension.app_protocol import create_app_protocol_extension_config_schema, app_protocol_schema_map from arkid.core.event import CREATE_APP, UPDATE_APP, DELETE_APP register_event(CREATE_APP, _('create app','创建应用')) register_event(UPDATE_APP, _('update app','修改应用')) register_event(DELETE_APP, _('delete app','删除应用')) -class AppConfigSchemaIn(Schema): - pass - -create_app_protocol_extension_config_schema( - AppConfigSchemaIn, -) +# class AppConfigSchemaIn(Schema): +# __root__: Union[tuple(app_protocol_schema_map.values())] = Field(discriminator='app_type') # type: ignore + # pass +# create_app_protocol_extension_config_schema( +# AppConfigSchemaIn, +# ) +AppConfigSchemaIn = create_app_protocol_extension_config_schema('AppConfigSchemaIn') class AppConfigSchemaOut(Schema): app_id: str diff --git a/arkid/core/extension/app_protocol.py b/arkid/core/extension/app_protocol.py index 8c56aca0e..27f8ddc1b 100644 --- a/arkid/core/extension/app_protocol.py +++ b/arkid/core/extension/app_protocol.py @@ -3,16 +3,19 @@ from typing import Optional from abc import abstractmethod from typing import Union, Literal +from typing_extensions import Annotated from ninja.orm import create_schema from arkid.core.extension import Extension from arkid.extension.models import TenantExtensionConfig from arkid.core.translation import gettext_default as _ from arkid.core.models import App from arkid.core import api as core_api, event as core_event +from pydantic import create_model as create_pydantic_model + app_protocol_schema_map = {} -def create_app_protocol_extension_config_schema(schema_cls, **field_definitions): +def create_app_protocol_extension_config_schema(schema_cls_name, **field_definitions): """创建应用协议类插件配置的Schema schema_cls只接受一个空定义的Schema @@ -30,14 +33,33 @@ def create_app_protocol_extension_config_schema(schema_cls, **field_definitions) schema_cls (ninja.Schema): 需要创建的Schema class field_definitions (Any): 任意数量的field,格式为: field_name=(field_type, Field(...)) """ - for schema in app_protocol_schema_map.values(): - core_api.add_fields(schema, **field_definitions) - if len(app_protocol_schema_map.values()) == 0: - core_api.add_fields(schema_cls) # type: ignore - elif len(app_protocol_schema_map.values()) == 1: - core_api.add_fields(schema_cls, __root__=app_protocol_schema_map.values()[0]) # type: ignore - else: - core_api.add_fields(schema_cls, data=(Union[tuple(app_protocol_schema_map.values())], Field(discriminator='app_type'))) # type: ignore + from django.db import models + class EmptyModel(models.Model): + pass + + temp = [] + for app_type, package_schema_map in app_protocol_schema_map.items(): + for schema in package_schema_map.values(): + core_api.add_fields(schema, **field_definitions) + # temp.append(Annotated[Union[tuple(package_schema_map.values())], Field(discriminator='package')]) # type: ignore + new_schema = create_schema(EmptyModel, + name=schema_cls_name, + exclude=['id'], + custom_fields=[ + # ("app_type", Literal[app_type], Field()), + ("__root__", Union[tuple(package_schema_map.values())], Field(discriminator='package')) # type: ignore + ], + ) + temp.append(new_schema) + + new_schema = create_schema(EmptyModel, + name=schema_cls_name, + exclude=['id'], + custom_fields=[ + ("__root__", Union[tuple(temp)], Field(discriminator='app_type')) # type: ignore + ], + ) + return new_schema class AppProtocolExtension(Extension): @@ -46,30 +68,27 @@ class AppProtocolExtension(Extension): def load(self): super().load() - self.listen_event(core_event.CREATE_APP, self.filter_event_handler) self.listen_event(core_event.UPDATE_APP, self.filter_event_handler) self.listen_event(core_event.DELETE_APP, self.filter_event_handler) - def register_config_schema(self, schema, app_type, package=None,**kwargs): # 父类 super().register_config_schema(schema, package, **kwargs) - - # app_type = kwargs.get('app_type', None) - # if app_type is None: - # raise Exception('') + package = package or self.package new_schema = create_schema(App, - name=self.package+'_config', - exclude=['is_del', 'is_active', 'updated', 'created', 'tenant', 'secret', 'type'], + name=package + '_' + app_type + '_config', + exclude=['is_del', 'is_active', 'updated', 'created', 'tenant', 'secret'], custom_fields=[ ("app_type", Literal[app_type], Field()), + ("package", Literal[package], Field()), ("config", schema, Field()) ], ) - app_protocol_schema_map[app_type] = new_schema + if app_type not in app_protocol_schema_map: + app_protocol_schema_map[app_type] = {} + app_protocol_schema_map[app_type][package] = new_schema self.app_type_map.append(app_type) - # def filter_event_handler(self, event, **kwargs): if event.data.app_type in self.app_type_map: @@ -82,7 +101,6 @@ def filter_event_handler(self, event, **kwargs): return self.delete_app(event, data.config) return False - @abstractmethod def create_app(self, event, config): pass diff --git a/arkid/redoc/view.py b/arkid/redoc/view.py index 54ebcf289..efa03cea6 100644 --- a/arkid/redoc/view.py +++ b/arkid/redoc/view.py @@ -2,6 +2,9 @@ from django.shortcuts import render from django.urls import reverse from django.views import View +from arkid.core.api import api +from django.http import HttpResponse +from ninja.responses import Response class Redoc(View): @@ -12,5 +15,16 @@ def get(self, request, *args, **kwargs): # pylint: disable=no-self-use unused #获得当前的HTTP或HTTPS host = request.META['HTTP_HOST'] #获取当前域名 - openapi_url = http + '://' + host + '/api/v1/openapi.json' - return render(request, 'redoc.html', context={'openapi_url':openapi_url}) \ No newline at end of file + openapi_url = http + '://' + host + '/api/v1/openapi_redoc.json' + # openapi_url = reverse('api/v1/openapi_redoc.json') + return render(request, 'redoc.html', context={'openapi_url':openapi_url}) + + +class RedocOpenAPI(View): + def get(self, request, *args, **kwargs): # pylint: disable=no-self-use unused-argument + schema = api.get_openapi_schema() + # delete discriminator + for value in schema['components']['schemas'].values(): + value.pop('discriminator', None) + return Response(schema) + diff --git a/arkid/urls.py b/arkid/urls.py index ed6c56c6a..00ff3a0ab 100644 --- a/arkid/urls.py +++ b/arkid/urls.py @@ -26,7 +26,8 @@ path("api/v1/", core_api.urls), path("api/v1/login", login_view.LoginEnter.as_view()), path("api/v1/login_process", login_view.LoginProcess.as_view()), - path("api/v1/redoc", redoc_view.Redoc.as_view()) + path("api/v1/redoc", redoc_view.Redoc.as_view()), + path("api/v1/openapi_redoc.json", redoc_view.RedocOpenAPI.as_view()) ] urlpatterns += core_urls.urlpatterns diff --git a/extension_root/com_longgui_oauth2_server/__init__.py b/extension_root/com_longgui_oauth2_server/__init__.py index 22ed55d0f..b9852e6c1 100644 --- a/extension_root/com_longgui_oauth2_server/__init__.py +++ b/extension_root/com_longgui_oauth2_server/__init__.py @@ -14,6 +14,8 @@ def load(self): # 加载相应的配置文件 self.register_config_schema(OIDCConfigSchema, 'OIDC', self.package) self.register_config_schema(Oauth2ConfigSchema, 'OAuth2' ,self.package) + self.register_config_schema(OIDCConfigSchema, 'OIDC', self.package+'2') + self.register_config_schema(Oauth2ConfigSchema, 'OAuth2' ,self.package+'2') def load_urls(self): diff --git a/templates/swagger.html b/templates/swagger.html new file mode 100644 index 000000000..7c6842296 --- /dev/null +++ b/templates/swagger.html @@ -0,0 +1,27 @@ + + + + + + NinjaAPI + + +
+
+ + + + +