Skip to content

Commit 84d8791

Browse files
authored
feat(apigw_manager/plugin): 新增 response_rewrite 和 redirect 插件的 build 函数 (#223)
1 parent 2bf406d commit 84d8791

2 files changed

Lines changed: 243 additions & 0 deletions

File tree

sdks/apigw-manager/src/apigw_manager/plugin/config.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,123 @@ def build_request_validation(
565565
}
566566

567567

568+
@dataclass
569+
class HeadersConfig:
570+
add: Dict[str, str]
571+
set: Dict[str, str]
572+
remove: List[str]
573+
574+
575+
def build_response_rewrite_validation(
576+
status_code: int,
577+
body: str,
578+
vars: Optional[str],
579+
body_base64: bool = False,
580+
headers: Optional[HeadersConfig] = None,
581+
) -> Dict[str, str]:
582+
"""generate response-rewrite plugin config
583+
584+
Args:
585+
status_code (int): 修改上游返回状态码,默认保留原始响应代码。
586+
body (str):
587+
- 修改上游返回的 body 内容,如果设置了新内容,header 里面的 content-length 字段也会被去掉。
588+
- 注意,这个字段只允许对插件配置中传递的主体进行解码,并不对上游响应进行解码。
589+
vars (str, Optional):
590+
- vars 是一个表达式列表,只有满足条件的请求和响应才会修改 body 和 header 信息,来自 lua-resty-expr。
591+
- 如果 vars 字段为空,那么所有的重写动作都会被无条件的执行。
592+
body_base64 (bool, Optional): 当设置时,在写给客户端之前,在body中传递的主体将被解码,这在一些图像和 Protobuffer 场景中使用。
593+
headers (HeadersConfig, optional):
594+
- add (Dict[str, str]): 添加新的响应头。格式为 {"name": "value", ...}。这个值能够以 $var 的格式包含 NGINX 变量。
595+
- set (Dict[str, str]): 改写响应头。格式为 {"name": "value", ...}。这个值能够以 $var 的格式包含 NGINX 变量。
596+
- remove (List[str]): 移除响应头。格式为 ["name", ...]。
597+
598+
Raises:
599+
ValueError: status_code must be between 200 and 598
600+
ValueError: key {} can not contain ':'
601+
602+
Returns:
603+
{
604+
"type": "response-rewrite",
605+
"yaml": "body: ''\nbody_base64: false\nheaders:\n add:\n - key: key1:value1\n remove:\n - key: key1\n set:\n - key: key1\n value: value1\nstatus_code: 200\nvars: ''\n"
606+
}
607+
"""
608+
609+
if not (200 <= status_code <= 598):
610+
raise ValueError("status_code must be between 200 and 598")
611+
612+
if vars:
613+
_check_vars(vars, "response_rewrite")
614+
615+
add_data = []
616+
for k, v in headers.add.items():
617+
if ":" in k:
618+
raise ValueError(f"key {k} can not contain ':'")
619+
add_data.append({"key": "{}:{}".format(k, v)})
620+
621+
set_data = []
622+
for k, v in headers.set.items():
623+
if ":" in k:
624+
raise ValueError(f"key {k} can not contain ':'")
625+
set_data.append({"key": k, "value": v})
626+
627+
remove_data = []
628+
for k in headers.remove:
629+
if ":" in k:
630+
raise ValueError(f"key {k} can not contain ':'")
631+
remove_data.append({"key": k})
632+
633+
return {
634+
"type": "response-rewrite",
635+
"yaml": yaml_dump(
636+
{
637+
"status_code": status_code,
638+
"body_base64": body_base64,
639+
"body": body,
640+
"vars": vars,
641+
"headers": {
642+
"add": add_data,
643+
"set": set_data,
644+
"remove": remove_data,
645+
}
646+
}
647+
),
648+
}
649+
650+
651+
def build_redirect_validation(
652+
uri: str,
653+
ret_code: int = 302,
654+
) -> Dict[str, str]:
655+
"""generate redirect plugin config
656+
657+
Args:
658+
uri (str): 要重定向到的 URI,可以包含 NGINX 变量。
659+
ret_code (int, Optional): HTTP 响应码。
660+
661+
Raises:
662+
ValueError: ret_code cannot be less than 200
663+
664+
Returns:
665+
{
666+
"type": "redirect",
667+
"yaml": "ret_code: 200\nuri: ''\n"
668+
}
669+
"""
670+
671+
if ret_code and ret_code < 200:
672+
raise ValueError("ret_code cannot be less than 200")
673+
674+
return {
675+
"type": "redirect",
676+
"yaml": yaml_dump(
677+
{
678+
"uri": uri,
679+
"ret_code": ret_code,
680+
}
681+
),
682+
}
683+
684+
568685
def _check_percentage(percentage: int, location: str):
569686
if percentage and not (0 < percentage <= 100):
570687
raise ValueError(f"The percentage of {location} must be greater than 0 and less than or equal to 100")

sdks/apigw-manager/tests/apigw_manager/plugin/test_config.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@
1818
build_api_breaker,
1919
build_fault_injection,
2020
build_request_validation,
21+
build_response_rewrite_validation,
22+
build_redirect_validation,
2123
build_stage_plugin_config_for_definition_yaml,
2224
UnhealthyConfig,
2325
HealthyConfig,
2426
AbortConfig,
2527
DelayConfig,
28+
HeadersConfig,
2629
)
2730

2831

@@ -479,6 +482,129 @@ def test_build_request_validation(
479482

480483
assert build_request_validation(body_schema, header_schema, rejected_msg, rejected_code) == expected
481484

485+
@pytest.mark.parametrize(
486+
"status_code, body, vars, body_base64, headers, will_error, expected",
487+
[
488+
(
489+
200,
490+
"",
491+
"",
492+
False,
493+
HeadersConfig(
494+
add={"key1": "value1"},
495+
set={"key1": "value1"},
496+
remove=["key1"],
497+
),
498+
False,
499+
{
500+
"type": "response-rewrite",
501+
"yaml": "body: ''\n"
502+
"body_base64: false\n"
503+
"headers:\n"
504+
" add:\n"
505+
" - key: key1:value1\n"
506+
" remove:\n"
507+
" - key: key1\n"
508+
" set:\n"
509+
" - key: key1\n value: value1\n"
510+
"status_code: 200\n"
511+
"vars: ''\n"
512+
},
513+
),
514+
(
515+
100,
516+
"",
517+
"",
518+
False,
519+
HeadersConfig(
520+
add={"key1": "value1"},
521+
set={"key1": "value1"},
522+
remove=["key1"],
523+
),
524+
True,
525+
None,
526+
),
527+
(
528+
200,
529+
"",
530+
"",
531+
False,
532+
HeadersConfig(
533+
add={"key1::": "value1"},
534+
set={},
535+
remove=[],
536+
),
537+
True,
538+
None,
539+
),
540+
(
541+
200,
542+
"",
543+
"",
544+
False,
545+
HeadersConfig(
546+
add={},
547+
set={"key1::": "value1"},
548+
remove=[],
549+
),
550+
True,
551+
None,
552+
),
553+
(
554+
200,
555+
"",
556+
"",
557+
False,
558+
HeadersConfig(
559+
add={},
560+
set={},
561+
remove=["key1::"],
562+
),
563+
True,
564+
None,
565+
),
566+
]
567+
)
568+
def test_build_response_rewrite_validation(
569+
self, status_code, body, vars, body_base64, headers, will_error, expected
570+
):
571+
if will_error:
572+
with pytest.raises(ValueError):
573+
build_response_rewrite_validation(status_code, body, vars, body_base64, headers)
574+
return
575+
576+
assert build_response_rewrite_validation(status_code, body, vars, body_base64, headers) == expected
577+
578+
@pytest.mark.parametrize(
579+
"uri, ret_code, will_error, expected",
580+
[
581+
(
582+
"",
583+
200,
584+
False,
585+
{
586+
"type": "redirect",
587+
"yaml": "ret_code: 200\nuri: ''\n"
588+
},
589+
),
590+
(
591+
"",
592+
100,
593+
True,
594+
None,
595+
),
596+
]
597+
)
598+
def test_build_redirect_validation(
599+
self, uri, ret_code, will_error, expected
600+
):
601+
if will_error:
602+
with pytest.raises(ValueError):
603+
build_redirect_validation(uri, ret_code)
604+
return
605+
606+
assert build_redirect_validation(uri, ret_code) == expected
607+
482608

483609
class TestBuildStagePluginConfigForDefinitionYaml:
484610
def test_build_stage_plugin_config_for_definition_yaml(self):

0 commit comments

Comments
 (0)