Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api/web/doc): doc example add schema example #734

Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ class SDKUsageExampleInputSLZ(serializers.Serializer):
)
stage_name = serializers.CharField(help_text="网关环境名称")
resource_name = serializers.CharField(help_text="资源名称")
# todo:暂时先不加
resource_id = serializers.IntegerField(help_text="资源id", required=False)


class SDKUsageExampleOutputSLZ(serializers.Serializer):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,21 @@
# We undertake not to change the open source license (MIT license) applicable
# to the current version of the project delivered to anyone in the future.
#

from django.template.loader import render_to_string
from django.utils.decorators import method_decorator
from django.utils.timezone import now as timezone_now
from drf_yasg.utils import swagger_auto_schema
from rest_framework import generics, status

from apigateway.apps.support.models import GatewaySDK
from apigateway.biz.resource_version import ResourceVersionHandler
from apigateway.biz.sdk.gateway_sdk import GatewaySDKHandler
from apigateway.biz.sdk.models import SDKDocContext
from apigateway.common.django.translation import get_current_language_code
from apigateway.common.permissions import GatewayDisplayablePermission
from apigateway.core.models import Release
from apigateway.utils import openapi
from apigateway.utils.responses import OKJsonResponse

from .serializers import SDKListInputSLZ, SDKUsageExampleInputSLZ, SDKUsageExampleOutputSLZ, StageSDKOutputSLZ
Expand Down Expand Up @@ -81,13 +85,31 @@ def retrieve(self, request, gateway_name: str, *args, **kwargs):
language=programming_language,
).last()

stage_name = slz.validated_data["stage_name"]
resource_name = slz.validated_data["resource_name"]
resource_id = slz.validated_data.get("resource_id")

resource_version_id = Release.objects.get_released_resource_version_id(request.gateway.id, stage_name)

example = {}
# 如果前端没有传resource_id,通过资源版本获取一下
if not resource_id and resource_version_id:
resource_id = ResourceVersionHandler.get_resource_id(resource_version_id, resource_name)
Han-Ya-Jun marked this conversation as resolved.
Show resolved Hide resolved
# 获取对应资源的schema
resource_schema = ResourceVersionHandler.get_resource_schema(resource_version_id, resource_id)
example = openapi.get_openapi_example(resource_schema)

content = render_to_string(
f"api_sdk/{get_current_language_code()}/{programming_language}_sdk_usage_example.md",
context=SDKDocContext(
gateway_name=request.gateway.name,
stage_name=slz.validated_data["stage_name"],
resource_name=slz.validated_data["resource_name"],
stage_name=stage_name,
resource_name=resource_name,
sdk_created_time=(sdk and sdk.created_time) or timezone_now(),
body_example=example.get("body_example", {}),
path_params=example.get("path_params", {}),
query_params=example.get("query_params", {}),
headers=example.get("headers", {}),
).as_dict(),
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#
# TencentBlueKing is pleased to support the open source community by making
# 蓝鲸智云 - API 网关(BlueKing - APIGateway) available.
# Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
# Licensed under the MIT License (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# http://opensource.org/licenses/MIT
#
# Unless required by applicable law or agreed to in writing, software distributed under
# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
# either express or implied. See the License for the specific language governing permissions and
# limitations under the License.
#
# We undertake not to change the open source license (MIT license) applicable
# to the current version of the project delivered to anyone in the future.
#
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#
# TencentBlueKing is pleased to support the open source community by making
# 蓝鲸智云 - API 网关(BlueKing - APIGateway) available.
# Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
# Licensed under the MIT License (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# http://opensource.org/licenses/MIT
#
# Unless required by applicable law or agreed to in writing, software distributed under
# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
# either express or implied. See the License for the specific language governing permissions and
# limitations under the License.
#
# We undertake not to change the open source license (MIT license) applicable
# to the current version of the project delivered to anyone in the future.
#
# myapp/templatetags/json_filters.py

import json

from django import template

register = template.Library()


@register.filter
def pretty_json(value, indent=2):
try:
return json.dumps(value, indent=indent)
except (ValueError, TypeError):
return value


@register.filter
def indent_json(value, indent=2):
try:
json_str = json.dumps(value, indent=indent)
lines = json_str.split("\n")
return "\n".join([lines[0]] + [" " * indent + line for line in lines[1:]])
except (ValueError, TypeError):
return value
14 changes: 13 additions & 1 deletion src/dashboard/apigateway/apigateway/biz/resource_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,9 @@ def get_resource_schema(resource_version_id: int, resource_id: int) -> dict:
"""
获取指定版本的资源对应的api schema
"""
resources_version_schema = OpenAPIResourceSchemaVersion.objects.get(resource_version_id=resource_version_id)
resources_version_schema = OpenAPIResourceSchemaVersion.objects.filter(
resource_version_id=resource_version_id
).first()
if resources_version_schema is None:
return {}
# 筛选资源数据
Expand All @@ -278,6 +280,16 @@ def get_resource_schema(resource_version_id: int, resource_id: int) -> dict:
return schema
return {}

@staticmethod
def get_resource_id(resource_version_id: int, resource_name: str) -> int:
resource_version = ResourceVersion.objects.filter(id=resource_version_id).first()
if not resource_version:
return 0
for resource in resource_version.data:
if resource["name"] == resource_name:
return resource["id"]
return 0
Han-Ya-Jun marked this conversation as resolved.
Show resolved Hide resolved


class ResourceDocVersionHandler:
@staticmethod
Expand Down
8 changes: 8 additions & 0 deletions src/dashboard/apigateway/apigateway/biz/sdk/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ class SDKDocContext:
resource_name: str
sdk_created_time: datetime = field(default_factory=timezone_now)
gateway_name_with_underscore: str = ""
body_example: dict = field(default_factory=dict)
path_params: dict = field(default_factory=dict)
query_params: dict = field(default_factory=dict)
headers: dict = field(default_factory=dict)
django_settings: Any = settings

def as_dict(self):
Expand All @@ -41,6 +45,10 @@ def as_dict(self):
"resource_name": self.resource_name,
"sdk_created_time": self.sdk_created_time,
"django_settings": self.django_settings,
"body_example": self.body_example,
"path_params": self.path_params,
"query_params": self.query_params,
"headers": self.headers,
}


Expand Down
1 change: 1 addition & 0 deletions src/dashboard/apigateway/apigateway/conf/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
"apigateway.account",
"apigateway.apps.feature",
"apigateway.apps.esb",
"apigateway.apps.docs",
"apigateway.apps.esb.bkcore",
"apigw_manager.apigw",
"apigateway.controller",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

{% load json_filters %}
创建网关 {{gateway_name}} Client 并调用 API 资源 {{resource_name}} 使用示例:

### 1. Django 项目
Expand All @@ -23,7 +23,13 @@ client = get_client_by_request(
)

# 请按具体场景修改请求
client.api.{{resource_name}}(...)
client.api.{{resource_name}}(
data={{ body_example|indent_json:4|safe }}, # 设置请求参数
path_params={{ path_params|indent_json:4|safe }}, # 设置路径参数
params={{ query_params|indent_json:4|safe }}, # 设置 querystring
headers={{ headers|indent_json:4|safe }}, # 设置请求头
timeout=10, # 设置当前请求超时
)
```

#### 1.2 get_client_by_username
Expand All @@ -44,7 +50,13 @@ client = get_client_by_username(
)

# 请按具体场景修改请求
client.api.{{resource_name}}(...)
client.api.{{resource_name}}(
data={{ body_example|indent_json:4|safe }}, # 设置请求参数
path_params={{ path_params|indent_json:4|safe }}, # 设置路径参数
params={{ query_params|indent_json:4|safe }}, # 设置 querystring
headers={{ headers|indent_json:4|safe }}, # 设置请求头
timeout=10, # 设置当前请求超时
)
```

### 2. 非 Django 项目
Expand All @@ -59,11 +71,11 @@ client = Client(
)

# 请求网关资源,请按具体场景修改请求,参数与 requests 保持兼容
client.api.{{resource_name}}(
data={...}, # 设置请求参数
path_params={...}, # 设置路径参数
params={...}, # 设置 querystring
headers={...}, # 设置请求头
client.api.{{ resource_name }}(
data={{ body_example|indent_json:4|safe }}, # 设置请求参数
path_params={{ path_params|indent_json:4|safe }}, # 设置路径参数
params={{ query_params|indent_json:4|safe }}, # 设置 querystring
headers={{ headers|indent_json:4|safe }}, # 设置请求头
timeout=10, # 设置当前请求超时
)
```
Expand Down
34 changes: 34 additions & 0 deletions src/dashboard/apigateway/apigateway/utils/openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import random

from faker import Faker
from openapi_schema_to_json_schema import to_json_schema

fake = Faker()

Expand Down Expand Up @@ -67,3 +68,36 @@ def handle_integer(schema):

def handle_default(schema):
return None


Han-Ya-Jun marked this conversation as resolved.
Show resolved Hide resolved
def generate_parameters_example(parameters):
path_params = {}
query_params = {}
headers = {}

for param in parameters:
name = param["name"]
example = param.get("example", "example_value")
if param["in"] == "path":
path_params[name] = example
elif param["in"] == "query":
query_params[name] = example
elif param["in"] == "header":
headers[name] = example

return path_params, query_params, headers


def get_openapi_example(schema):
example = {}
request_body = schema.get("requestBody")
if request_body and "content" in request_body and "application/json" in request_body["content"]:
# todo: 暂时在只支持application/json
json_schema = to_json_schema(request_body["content"]["application/json"]["schema"])
example["body_example"] = generate_example(json_schema)
parameters = schema.get("parameters", [])
path, query, headers = generate_parameters_example(parameters)
example["path_params"] = path
example["query_params"] = query
example["headers"] = headers
return example
Loading