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: 规则审计-基础代码构建-SQL 生成器 --story=121357782 #487

Merged
merged 1 commit into from
Dec 26, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions src/backend/.djangoignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
venv/
debug/
17 changes: 17 additions & 0 deletions src/backend/core/sql/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
"""
TencentBlueKing is pleased to support the open source community by making
蓝鲸智云 - 审计中心 (BlueKing - Audit Center) available.
Copyright (C) 2023 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.
"""
107 changes: 107 additions & 0 deletions src/backend/core/sql/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# -*- coding: utf-8 -*-
"""
TencentBlueKing is pleased to support the open source community by making
蓝鲸智云 - 审计中心 (BlueKing - Audit Center) available.
Copyright (C) 2023 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.
"""

from django.utils.translation import gettext_lazy
from pypika import functions as fn

from core.choices import TextChoices


class FilterConnector(TextChoices):
"""
Filter Connector
"""

AND = "and", gettext_lazy("AND")
OR = "or", gettext_lazy("OR")


class FieldType(TextChoices):
"""
字段类型
"""

STRING = "string", gettext_lazy("字符串")
DOUBLE = "double", gettext_lazy("浮点数")
INT = "int", gettext_lazy("整数")
LONG = "long", gettext_lazy("长整数")
TEXT = "text", gettext_lazy("文本")


class AggregateType(TextChoices):
"""
聚合类型
"""

COUNT = "COUNT", gettext_lazy("计数")
SUM = "SUM", gettext_lazy("求和")
AVG = "AVG", gettext_lazy("平均值")
MAX = "MAX", gettext_lazy("最大值")
MIN = "MIN", gettext_lazy("最小值")
DISCOUNT = "DISCOUNT", gettext_lazy("去重计数")

@classmethod
def get_function(cls, aggregate_type: str):
"""根据聚合类型返回 PyPika 对应的函数"""

from core.sql.functions import DisCount

aggregate_mapping = {
cls.COUNT.value: fn.Count,
cls.SUM.value: fn.Sum,
cls.AVG.value: fn.Avg,
cls.MAX.value: fn.Max,
cls.MIN.value: fn.Min,
cls.DISCOUNT.value: DisCount,
}
if aggregate_type not in aggregate_mapping:
raise ValueError(f"不支持的聚合类型: {aggregate_type}")
return aggregate_mapping[aggregate_type]


class JoinType(TextChoices):
"""
连接类型
"""

INNER_JOIN = "inner_join", gettext_lazy("内连接")
LEFT_JOIN = "left_join", gettext_lazy("左连接")
RIGHT_JOIN = "right_join", gettext_lazy("右连接")
FULL_OUTER_JOIN = "full_outer_join", gettext_lazy("全连接")


class Operator(TextChoices):
"""匹配符"""

EQ = "eq", gettext_lazy("Equal")
NEQ = "neq", gettext_lazy("NotEqual")
REG = "reg", gettext_lazy("Regex")
NREG = "nreg", gettext_lazy("NotRegex")
INCLUDE = "include", gettext_lazy("Include")
EXCLUDE = "exclude", gettext_lazy("Exclude")

@classmethod
def match_handler(cls, operator: str):
return {
cls.EQ: lambda field, value: field == value,
cls.NEQ: lambda field, value: field != value,
cls.INCLUDE: lambda field, value: field.isin(value),
cls.EXCLUDE: lambda field, value: ~field.isin(value),
cls.REG: lambda field, value: field.regex(value),
cls.NREG: lambda field, value: ~field.regex(value),
}.get(operator)
72 changes: 72 additions & 0 deletions src/backend/core/sql/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# -*- coding: utf-8 -*-
"""
TencentBlueKing is pleased to support the open source community by making
蓝鲸智云 - 审计中心 (BlueKing - Audit Center) available.
Copyright (C) 2023 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.
"""

from django.utils.translation import gettext_lazy


class SQLGeneratorError(Exception):
"""基础异常类,用于所有 SQLGenerator 相关的异常。"""


class TableNotRegisteredError(SQLGeneratorError):
"""当请求的表未在配置中注册时抛出。"""

MESSAGE = gettext_lazy("表 '{table_name}' 未在配置中声明。")

def __init__(self, table_name: str):
message = self.MESSAGE.format(table_name=table_name)
super().__init__(message)


class UnsupportedJoinTypeError(SQLGeneratorError):
"""当使用了不支持的 JOIN 类型时抛出。"""

MESSAGE = gettext_lazy("不支持的 JOIN 类型:'{join_type}'。")

def __init__(self, join_type: str):
message = self.MESSAGE.format(join_type=join_type)
super().__init__(message)


class UnsupportedOperatorError(SQLGeneratorError):
"""当使用了不支持的操作符时抛出。"""

MESSAGE = gettext_lazy("不支持的操作符:'{operator}'。")

def __init__(self, operator: str):
message = self.MESSAGE.format(operator=operator)
super().__init__(message)


class InvalidAggregateTypeError(SQLGeneratorError):
"""当使用了无效的聚合类型时抛出。"""

MESSAGE = gettext_lazy("不支持的聚合类型:'{aggregate_type}'。")

def __init__(self, aggregate_type: str):
message = self.MESSAGE.format(aggregate_type=aggregate_type)
super().__init__(message)


class MissingFromOrJoinError(SQLGeneratorError):
"""当既未指定 from_table 也未指定 join_tables 时抛出。"""

MESSAGE = gettext_lazy("配置中必须指定 `from_table` 或 `join_tables`。")

def __init__(self):
super().__init__(self.MESSAGE)
28 changes: 28 additions & 0 deletions src/backend/core/sql/functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
"""
TencentBlueKing is pleased to support the open source community by making
蓝鲸智云 - 审计中心 (BlueKing - Audit Center) available.
Copyright (C) 2023 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.
"""
from pypika.functions import Count


class DisCount(Count):
"""
去重计数
"""

def __init__(self, param, alias=None):
super().__init__(param, alias=alias)
self.distinct()
115 changes: 115 additions & 0 deletions src/backend/core/sql/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# -*- coding: utf-8 -*-
"""
TencentBlueKing is pleased to support the open source community by making
蓝鲸智云 - 审计中心 (BlueKing - Audit Center) available.
Copyright (C) 2023 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.
"""
from typing import List, Optional, Union

from pydantic import BaseModel
from pydantic import Field as PydanticField
from pypika import Order as PypikaOrder

from core.sql.constants import (
AggregateType,
FieldType,
FilterConnector,
JoinType,
Operator,
)


class Field(BaseModel):
"""
字段
"""

table: str # 表名
raw_name: str # 原始字段名称
display_name: str # 作为 sql 的列名,唯一
field_type: FieldType # 字段类型
aggregate: AggregateType = None # 聚合函数


class LinkField(BaseModel):
"""
关联字段
"""

left_field: str # 左表关联字段
right_field: str # 右表关联字段


class JoinTable(BaseModel):
"""
联表
"""

join_type: JoinType # 连接类型
link_fields: List[LinkField] # 连接字段
left_table: str # 左表
right_table: str # 右表


class Condition(BaseModel):
"""
条件
"""

field: Field # 字段
operator: Operator # 操作符
filters: List[Union[str, int, float]] = PydanticField(default_factory=list) # 多个筛选值 - 如果操作符需要操作多个值,使用该字段
filter: Union[str, int, float] = PydanticField(default_factory=str) # 筛选值 - 如果操作符只需要一个值,使用该字段


class WhereCondition(BaseModel):
"""
筛选条件
"""

connector: FilterConnector = FilterConnector.AND # 连接符
conditions: List["WhereCondition"] = PydanticField(default_factory=list) # 子条件
condition: Optional[Condition] = None # 条件


class Order(BaseModel):
"""
排序
"""

field: Field # 字段
order: PypikaOrder # 排序方式


class Pagination(BaseModel):
"""
分页
"""

limit: Optional[int] = None # 限制数量
offset: Optional[int] = None # 偏移量


class SqlConfig(BaseModel):
"""
sql 配置
"""

select_fields: List[Field] # 作为 sql 的列
from_table: str # 主表
join_tables: Optional[List[JoinTable]] = None # 联表
where: Optional[WhereCondition] = None # 筛选条件
group_by: List[Field] = PydanticField(default_factory=list) # 分组条件;如果未指定但有聚合函数,则会自动添加 group by 条件
order_by: List[Order] = PydanticField(default_factory=list) # 排序条件
pagination: Optional[Pagination] = None # 分页条件
Loading
Loading