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:mock plugin #894

Merged
merged 3 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -30,6 +30,7 @@ class PluginTypeCodeEnum(StructuredEnum):
BK_VERIFIED_USER_EXEMPTED_APPS = EnumField(
"bk-verified-user-exempted-apps", label=_("免用户认证应用白名单(不推荐)")
)
BK_MOCK = EnumField("bk-mock", label=_("mocking 插件"))


class PluginTypeScopeEnum(StructuredEnum):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,31 @@ def convert(self, config: Dict[str, Any]) -> Dict[str, Any]:
return config


class BkMockConvertor(PluginConvertor):
plugin_type_code: ClassVar[PluginTypeCodeEnum] = PluginTypeCodeEnum.BK_MOCK

"""
将config转换如下:
{
"response_status": 200,
"response_example": "......."
"response_headers": [{"aaa": "bbb"}, {"ccc": "ddd"}] ==>> "response_headers": {"aaa": "bbb", "ccc": "ddd"}
}
"""

def convert(self, config: Dict[str, Any]) -> Dict[str, Any]:
config["response_headers"] = {item["key"]: item["value"] for item in config["response_headers"]}
Lawrence-lkq marked this conversation as resolved.
Show resolved Hide resolved
return config


class PluginConvertorFactory:
plugin_convertors: ClassVar[Dict[PluginTypeCodeEnum, PluginConvertor]] = {
c.plugin_type_code: c
for c in [
HeaderWriteConvertor(),
IPRestrictionConvertor(),
BkCorsConvertor(),
BkMockConvertor(),
]
}

Expand Down
203 changes: 202 additions & 1 deletion src/dashboard/apigateway/apigateway/fixtures/plugins.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@
"properties": {
"allow_origins": {
"description": "Origins to allow CORS. Use the scheme://host:port format. For example, https://example.com:8081. If you have multiple origins, use a , to list them. If allow_credential is set to false, you can enable CORS for all origins by using *. If allow_credential is set to true, you can forcefully allow CORS on all origins by using ** but it will pose some security issues. allow_origins, allow_origins_by_regex can only be one valid at a time.",
"type": "string",
"type": "string",
Lawrence-lkq marked this conversation as resolved.
Show resolved Hide resolved
"pattern": "^(|\\*|\\*\\*|null|http(s)?://[-a-zA-Z0-9:\\[\\]\\.]+(,http(s)?://[-a-zA-Z0-9:\\[\\]\\.]+)*)$",
"default": ""
},
Expand Down Expand Up @@ -1370,3 +1370,204 @@
- bk-verified-user-exempted-apps
- plugin
- '0'
- model: schema.schema
fields:
created_time: 2024-09-03 15:32:21.010753+00:00
updated_time: 2024-09-03 15:32:21.010753+00:00
name: bk-mock
type: plugin
version: '0'
_schema: |-
{
"schema": {
"type": "object",
"properties": {
"response_status": {
"type": "integer",
"default": 200,
"title": "response_status",
"minimum": 100
},
"response_example": {
"title": "response_example",
"type": "string",
"ui:component": {
"type": "textarea",
"rows": 10
}
},
"response_headers": {
"description": "Set the response headers",
"title": "response_headers",
"type": "array",
"ui:component": {
"name": "bfArray"
},
"items": {
"properties": {
"key": {
"title": "key",
"description": "You can set the key of the response header. The key of the response header cannot contain a colon (:).",
"type": "string",
"maxLength": 1024,
"pattern": "^[\\w-]+$",
"ui:rules": ["required"]
},
"value": {
"title": "value",
"description": "You can set the value of the response header.",
"type": "string",
"maxLength": 1024,
"pattern": "^[\\s\\S]*$",
"ui:rules": ["required"]
}
},
"type": "object"
}
}
}
},
"layout": [],
"formData": {},
"rules": {}
}
description: '-'
example: '-'
- model: plugin.plugintype
fields:
code: bk-mock
name: mocking 插件
name_en: Mock Plugin
is_public: true
scope: resource
schema:
- bk-mock
- plugin
- '0'
- model: plugin.pluginform
fields:
language: ''
type:
- bk-mock
notes: 支持模拟路由的响应,可以模拟响应的状态、正文和报头。
Lawrence-lkq marked this conversation as resolved.
Show resolved Hide resolved
example: '作用:资源绑定这个插件,用户请求这个资源的时候,返回的是这个插件配置的响应体/状态码以及响应头\n\n响应状态码: 200\n响应体: {"example": {"aaa": "bbb"}}\n响应头: [{"key": "example1", "value": "11"}, {"key": "example2", "value": "22"}]'
Lawrence-lkq marked this conversation as resolved.
Show resolved Hide resolved
style: dynamic
default_value: ''
config: |-
{
"schema": {
"type": "object",
"properties": {
"response_status": {
"type": "integer",
"default": 200,
"title": "响应状态码",
"minimum": 100
},
"response_example": {
"title": "响应体",
"type": "string",
"ui:component": {
"type": "textarea",
"rows": 10
}
},
"response_headers": {
"description": "设置响应头",
"items": {
"properties": {
"key": {
"title": "键",
"description": "You can set the key of the request header. The key of the request header cannot contain a colon (:).",
"type": "string",
"maxLength": 1024,
"pattern": "^[\\w-]+$",
"ui:rules": ["required"]
},
"value": {
"title": "值",
"description": "You can set the value of the request header.",
"type": "string",
"maxLength": 1024,
"pattern": "^[\\s\\S]*$",
"ui:rules": ["required"]
}
},
"type": "object"
},
"minItems": 0,
"title": "响应头",
"type": "array",
"ui:component": {
"name": "bfArray"
}
}
}
},
"layout": [],
"formData": {},
"rules": {}
}
- model: plugin.pluginform
fields:
language: 'en'
type:
- bk-mock
notes: for mocking an API, it returns data in the format specified and the request is not forwarded to the Upstream.
example: 'Function: Resource binding this plug-in, when the user requests this resource, the return is the plug-in configuration of the response body/status code and response header \n\n Response status code: 200\n response body: {"example": {"aaa": "bbb"}}\n response header: [{"key": "example1", "value": "11"}, {"key": "example2", "value": "22"}]'
style: dynamic
default_value: ''
config: |-
{
"schema": {
"type": "object",
"properties": {
"response_status": {
"type": "integer",
"default": 200,
"title": "response_status",
"minimum": 100
},
"response_example": {
"title": "response_example",
"type": "string",
"ui:component": {
"type": "textarea",
"rows": 10
}
},
"response_headers": {
"description": "Set the response headers",
"title": "response_headers",
"type": "array",
"ui:component": {
"name": "bfArray"
},
"items": {
"properties": {
"key": {
"title": "key",
"description": "You can set the key of the response header. The key of the response header cannot contain a colon (:).",
"type": "string",
"maxLength": 1024,
"pattern": "^[\\w-]+$",
"ui:rules": ["required"]
},
"value": {
"title": "value",
"description": "You can set the value of the response header.",
"type": "string",
"maxLength": 1024,
"pattern": "^[\\s\\S]*$",
"ui:rules": ["required"]
}
},
"type": "object"
}
}
}
},
"layout": [],
"formData": {},
"rules": {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from apigateway.apps.plugin.constants import PluginTypeCodeEnum
from apigateway.common.plugin.plugin_convertors import (
BkCorsConvertor,
BkMockConvertor,
DefaultPluginConvertor,
HeaderWriteConvertor,
IPRestrictionConvertor,
Expand Down Expand Up @@ -175,6 +176,54 @@ def test_convert(self, data, expected):
assert result == expected


class TestBkMockConvertor:
@pytest.mark.parametrize(
"data, expected",
[
(
{
"response_status": 200,
"response_example": "mock1",
"response_headers": [],
},
{
"response_status": 200,
"response_example": "mock1",
"response_headers": {},
},
),
(
{
"response_status": 200,
"response_example": "mock1",
"response_headers": [{"key": "mock1", "value": "mock1"}, {"key": "mock2", "value": "mock2"}],
},
{
"response_status": 200,
"response_example": "mock1",
"response_headers": {"mock1": "mock1", "mock2": "mock2"},
},
),
(
{
"response_status": 200,
"response_example": "mock1",
"response_headers": [{"key": "mock1", "value": "mock1"}, {"key": "mock1", "value": "mock2"}],
},
{
"response_status": 200,
"response_example": "mock1",
"response_headers": {"mock1": "mock2"},
},
),
],
)
def test_convert(self, data, expected):
convertor = BkMockConvertor()
result = convertor.convert(data)
assert result == expected


class TestPluginConvertorFactory:
def test_get_convertor(self):
convertor = PluginConvertorFactory.get_convertor(PluginTypeCodeEnum.BK_IP_RESTRICTION)
Expand Down
Loading