Skip to content

Commit

Permalink
Merge pull request #783 from longguikeji/feature-app-schema
Browse files Browse the repository at this point in the history
fix: 🐛 create API schema
  • Loading branch information
luolu-lg authored Apr 20, 2022
2 parents 776a9d4 + 184d3f1 commit 0068258
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 30 deletions.
16 changes: 9 additions & 7 deletions api/v1/views/app.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
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
from django.db import transaction
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
Expand Down
58 changes: 38 additions & 20 deletions arkid/core/extension/app_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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):
Expand All @@ -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:
Expand All @@ -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
Expand Down
18 changes: 16 additions & 2 deletions arkid/redoc/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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})
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)

3 changes: 2 additions & 1 deletion arkid/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 2 additions & 0 deletions extension_root/com_longgui_oauth2_server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
27 changes: 27 additions & 0 deletions templates/swagger.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<link type="text/css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@latest/swagger-ui.css">
<link rel="shortcut icon" href="https://ninja.rest-framework.com/favicon.png">
<title>NinjaAPI</title>
</head>
<body>
<div id="swagger-ui">
</div>

<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@latest/swagger-ui-bundle.js"></script>
<script>
const ui = SwaggerUIBundle({
url: '{{openapi_url}}',
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
],
layout: "BaseLayout",

deepLinking: true
})
</script>
</body>
</html>

0 comments on commit 0068258

Please sign in to comment.