From 7afc1324591e6246873aa152e4c8dfd054aea2d4 Mon Sep 17 00:00:00 2001 From: raja <1647193241@qq.com> Date: Thu, 9 Jan 2025 16:53:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=A7=84=E5=88=99=E5=AE=A1=E8=AE=A1-?= =?UTF-8?q?=E7=AD=96=E7=95=A5=E6=96=B0=E5=A2=9E/=E7=BC=96=E8=BE=91-?= =?UTF-8?q?=E5=90=8E=E7=AB=AF=E6=8E=A5=E5=8F=A3=20--story=3D121513458?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 【新增】 1. 策略 link_table 支持为 null 2. 调整规则审计策略 SQL --- src/backend/core/sql/sql_builder.py | 6 ++++- .../web/strategy_v2/handlers/rule_audit.py | 20 ++++++++++++++--- .../services/web/strategy_v2/resources.py | 4 ++-- .../services/web/strategy_v2/serializers.py | 2 +- .../test_rule_audit_sql_formatter.py | 22 +++++++++---------- 5 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/backend/core/sql/sql_builder.py b/src/backend/core/sql/sql_builder.py index ecab32a3..874b22c8 100644 --- a/src/backend/core/sql/sql_builder.py +++ b/src/backend/core/sql/sql_builder.py @@ -38,6 +38,8 @@ class SQLGenerator: """SQL 生成器""" + table_cls = Table + def __init__(self, query_builder: QueryBuilder, config: SqlConfig): """ 初始化生成器 @@ -64,7 +66,9 @@ def _register_tables(self): register_tables[alias] = table # 更新 table_map 映射 - self.table_map.update({alias: Table(table.table_name).as_(alias) for alias, table in register_tables.items()}) + self.table_map.update( + {alias: self.table_cls(table.table_name).as_(alias) for alias, table in register_tables.items()} + ) def _get_table(self, table: Union[str, SqlTable]) -> Table: """根据表名获取 Table 对象""" diff --git a/src/backend/services/web/strategy_v2/handlers/rule_audit.py b/src/backend/services/web/strategy_v2/handlers/rule_audit.py index a3a437cd..8fbaed24 100644 --- a/src/backend/services/web/strategy_v2/handlers/rule_audit.py +++ b/src/backend/services/web/strategy_v2/handlers/rule_audit.py @@ -15,11 +15,12 @@ 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 typing import Dict, List, Optional +from typing import Any, Dict, List, Optional from django.conf import settings from django.shortcuts import get_object_or_404 from pydantic import BaseModel +from pypika import Table as PyPikaTable from pypika import functions as fn from pypika.terms import Function, Term, ValueWrapper @@ -43,6 +44,17 @@ from services.web.strategy_v2.models import LinkTable, Strategy +class RuleAuditTable(PyPikaTable): + def get_sql(self, **kwargs: Any) -> str: + kwargs["quote_char"] = None + kwargs["alias_quote_char"] = "`" + return super().get_sql(**kwargs) + + +class RuleAuditSqlGenerator(SQLGenerator): + table_cls = RuleAuditTable + + class UdfBuildOriginData(Function): """ """ @@ -87,7 +99,7 @@ class FieldMap(BaseModel): target_value: Optional[str] = None # 固定值 -class RuleAuditSQLGenerator: +class RuleAuditSQLBuilder: """ 规则审计 SQL 生成器 """ @@ -241,7 +253,9 @@ def build_sql(self) -> str: } # 1. 生成子查询 (sub_table) sql_config = self.format(config_json) - sub_table = SQLGenerator(query_builder=self.query_builder, config=sql_config).generate().as_("sub_table") + sub_table = ( + RuleAuditSqlGenerator(query_builder=self.query_builder, config=sql_config).generate().as_("sub_table") + ) # 2. 构造 JSON_OBJECT(...) 参数 json_obj_args = {field.display_name: sub_table.field(field.display_name) for field in sql_config.select_fields} # 3. 最外层 select 列表 diff --git a/src/backend/services/web/strategy_v2/resources.py b/src/backend/services/web/strategy_v2/resources.py index 0c0db574..bd5f4a41 100644 --- a/src/backend/services/web/strategy_v2/resources.py +++ b/src/backend/services/web/strategy_v2/resources.py @@ -93,7 +93,7 @@ StrategyPendingError, StrategyTypeCanNotChange, ) -from services.web.strategy_v2.handlers.rule_audit import RuleAuditSQLGenerator +from services.web.strategy_v2.handlers.rule_audit import RuleAuditSQLBuilder from services.web.strategy_v2.models import ( LinkTable, LinkTableTag, @@ -166,7 +166,7 @@ def build_rule_audit_sql(self, strategy: Strategy) -> str: 构建规则审计SQL """ - return RuleAuditSQLGenerator(strategy).build_sql() + return RuleAuditSQLBuilder(strategy).build_sql() class CreateStrategy(StrategyV2Base): diff --git a/src/backend/services/web/strategy_v2/serializers.py b/src/backend/services/web/strategy_v2/serializers.py index 49893c05..17f66148 100644 --- a/src/backend/services/web/strategy_v2/serializers.py +++ b/src/backend/services/web/strategy_v2/serializers.py @@ -905,7 +905,7 @@ class RuleAuditDataSourceSerializer(serializers.Serializer): system_ids = serializers.ListField( label=gettext_lazy("System ID"), child=serializers.CharField(), required=False, allow_empty=True ) - link_table = RuleAuditLinkTableSerializer(label=gettext_lazy("Link Table"), required=False) + link_table = RuleAuditLinkTableSerializer(label=gettext_lazy("Link Table"), required=False, allow_null=True) display_name = serializers.CharField(label=gettext_lazy("Display Name"), required=False) def validate(self, attrs): diff --git a/src/backend/tests/test_strategy_v2/test_rule_audit_sql_formatter.py b/src/backend/tests/test_strategy_v2/test_rule_audit_sql_formatter.py index 1325d7b0..aad20e52 100644 --- a/src/backend/tests/test_strategy_v2/test_rule_audit_sql_formatter.py +++ b/src/backend/tests/test_strategy_v2/test_rule_audit_sql_formatter.py @@ -21,7 +21,7 @@ from services.web.strategy_v2.constants import LinkTableTableType, RuleAuditConfigType from services.web.strategy_v2.exceptions import LinkTableConfigError -from services.web.strategy_v2.handlers.rule_audit import RuleAuditSQLGenerator +from services.web.strategy_v2.handlers.rule_audit import RuleAuditSQLBuilder from services.web.strategy_v2.models import Strategy from tests.base import TestCase @@ -32,7 +32,7 @@ class TestRuleAuditSQLFormatter(TestCase): """ def _build_and_assert_sql(self, strategy: Strategy, expected_sql: str, mock_link_table_obj=None): - formatter = RuleAuditSQLGenerator(strategy) + formatter = RuleAuditSQLBuilder(strategy) if mock_link_table_obj: with patch( "services.web.strategy_v2.handlers.rule_audit.get_object_or_404", return_value=mock_link_table_obj @@ -72,7 +72,7 @@ def test_single_table_no_where_no_system_ids(self): "`event_data`,200 `strategy_id` " "FROM (" "SELECT `simple_rt`.`fieldA` `字段A` " - "FROM `simple_rt` `simple_rt`) `sub_table`" + "FROM simple_rt `simple_rt`) `sub_table`" ) self._build_and_assert_sql(strategy, expected_sql) @@ -116,7 +116,7 @@ def test_single_table_with_where_and_system_ids(self): "`event_data`,101 `strategy_id` " "FROM (" "SELECT `test_rt_id`.`event_id` `事件ID` " - "FROM `test_rt_id` `test_rt_id` " + "FROM test_rt_id `test_rt_id` " "WHERE `test_rt_id`.`username`='admin' AND `test_rt_id`.`system_id` IN ('sys_1','sys_2')) `sub_table`" ) self._build_and_assert_sql(strategy, expected_sql) @@ -157,7 +157,7 @@ def test_single_table_with_field_mapping(self): "udf_build_origin_data('列A|!@#$%^&*|列B'," "CONCAT_WS('',CAST(`sub_table`.`列A` AS STRING),'|!@#$%^&*|',CAST(`sub_table`.`列B` AS STRING))) " "`event_data`,300 `strategy_id`,'abcdef' `fixed_col`,`sub_table`.`列B` `mapped_col` " - "FROM (SELECT `my_rt`.`colA` `列A`,`my_rt`.`colB` `列B` FROM `my_rt` `my_rt`) `sub_table`" + "FROM (SELECT `my_rt`.`colA` `列A`,`my_rt`.`colB` `列B` FROM my_rt `my_rt`) `sub_table`" ) self._build_and_assert_sql(strategy, expected_sql) @@ -208,8 +208,8 @@ def test_link_table_simple(self, mock_get_obj): "`event_data`,999 `strategy_id`,`sub_table`.`username` `operator_name`,'123' `bk_biz_id` " "FROM (" "SELECT `log_rt_1`.`event_id` `事件ID` " - "FROM `log_rt_1` `log_rt_1` " - "LEFT JOIN `asset_rt_2` `asset_rt_2` " + "FROM log_rt_1 `log_rt_1` " + "LEFT JOIN asset_rt_2 `asset_rt_2` " "ON `log_rt_1`.`event_id`=`asset_rt_2`.`resource_id` " "WHERE `log_rt_1`.`system_id` " "IN ('sys_111')) `sub_table`" @@ -235,7 +235,7 @@ def test_link_table_config_empty_links(self): with patch("services.web.strategy_v2.handlers.rule_audit.get_object_or_404", return_value=mock_link_table_obj): with self.assertRaises(LinkTableConfigError): - RuleAuditSQLGenerator(strategy).build_sql() + RuleAuditSQLBuilder(strategy).build_sql() def test_json_with_mixed_columns_and_values(self): """ @@ -274,7 +274,7 @@ def test_json_with_mixed_columns_and_values(self): "'列A|!@#$%^&*|列B'," "CONCAT_WS('',CAST(`sub_table`.`列A` AS STRING),'|!@#$%^&*|',CAST(`sub_table`.`列B` AS STRING))) " "`event_data`,400 `strategy_id`,'固定值' `fixed_value`,`sub_table`.`列A` `mapped_col` " - "FROM (SELECT `mixed_rt`.`colA` `列A`,`mixed_rt`.`colB` `列B` FROM `mixed_rt` `mixed_rt`) `sub_table`" + "FROM (SELECT `mixed_rt`.`colA` `列A`,`mixed_rt`.`colB` `列B` FROM mixed_rt `mixed_rt`) `sub_table`" ) self._build_and_assert_sql(strategy, expected_sql) @@ -305,7 +305,7 @@ def test_json_with_special_characters(self): "SELECT " "udf_build_origin_data('列A',CONCAT_WS('',CAST(`sub_table`.`列A` AS STRING))) " "`event_data`,500 `strategy_id`,'值含\"特殊字符\\\"和反斜杠' `fixed_col` " - "FROM (SELECT `special_char_rt`.`colA` `列A` FROM `special_char_rt` `special_char_rt`) `sub_table`" + "FROM (SELECT `special_char_rt`.`colA` `列A` FROM special_char_rt `special_char_rt`) `sub_table`" ) self._build_and_assert_sql(strategy, expected_sql) @@ -342,7 +342,7 @@ def test_nested_json_structure(self): "SELECT " "udf_build_origin_data('列A',CONCAT_WS('',CAST(`sub_table`.`列A` AS STRING))) " "`event_data`,600 `strategy_id` " - "FROM (SELECT `nested_rt`.`colA` `列A` FROM `nested_rt` `nested_rt`) `sub_table`" + "FROM (SELECT `nested_rt`.`colA` `列A` FROM nested_rt `nested_rt`) `sub_table`" ) self._build_and_assert_sql(strategy, expected_sql)