Skip to content

feat: 支持空间变量 --story=132692335#633

Open
guohelu wants to merge 4 commits intoTencentBlueKing:developfrom
guohelu:develop_0313
Open

feat: 支持空间变量 --story=132692335#633
guohelu wants to merge 4 commits intoTencentBlueKing:developfrom
guohelu:develop_0313

Conversation

@guohelu
Copy link
Collaborator

@guohelu guohelu commented Mar 13, 2026

Reviewed, transaction id: 75794

# Reviewed, transaction id: 75794
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review 总结

本 PR 新增了 variable_manager 模块,实现空间级变量的 CRUD 接口,并将任务启动时的空间变量注入从 EngineSpaceConfig 迁移到新的变量服务。整体结构清晰,文档齐全,迁移文件也已同步。

以下几个问题需要关注:


⚠️ 逻辑问题 — start() 缺少错误处理,API 失败会直接抛出 KeyError

文件: bkflow/task/operations.py,新增行:

space_var = InterfaceModuleClient().get_variable(self.task_instance.space_id)
root_pipeline_context.update(space_var["data"])

get_variable 接口请求失败或返回非预期结构(如无 "data" 字段),space_var["data"] 会直接抛出 KeyError,导致任务启动失败且无日志。建议加 .get("data", {}) 并在接口调用前后添加日志。


⚠️ 逻辑问题 — get_variable_list 使用 require_POST 语义不符

文件: bkflow/apigw/views/get_variable_list.py
查询接口使用了 @require_POST,但按 REST 规范查询应使用 GET(规范第 4 节)。若因兼容网关限制必须用 POST,建议在注释里说明原因。


⚠️ 逻辑问题 — validate_type 使用硬编码字符串,应抽为常量

文件: bkflow/variable_manager/serializers.py,第 57 行:

if value not in ["space", "scope"]:

"space""scope" 是业务关键枚举值,应在 constants.py 中定义为 VARIABLE_TYPE_SPACE = "space" 等常量,与规范第 2 节一致。


⚠️ 逻辑问题 — create 方法中 Space 存在性校验应移到 validate 而非 create

文件: bkflow/variable_manager/serializers.py,第 63-65 行:

def create(self, validated_data):
    ...
    if not Space.objects.filter(id=space_id).exists():
        raise serializers.ValidationError(...)

create() 是持久化方法,校验逻辑应在 validate()validate_space_id() 中完成(规范第 1 节),否则 is_valid() 通过后调用 save() 才抛出校验异常,行为反直觉。


🔒 安全问题 — VariableInternalViewSet.get_variable 缺少 is_deleted 过滤

文件: bkflow/variable_manager/views.py,第 63 行:

variables = self.queryset.filter(space_id=space_id, type="space").only("key", "value")

queryset = VariableManager.objects.all() 未过滤软删除数据,已删除的变量依然会被注入到任务上下文中,可能导致意外行为。应加 is_deleted=False 过滤条件。


⚠️ 逻辑问题 — delete_variable 未过滤软删除记录

文件: bkflow/apigw/views/delete_variable.py,第 40 行:

variable = VariableManager.objects.get(id=variable_id, space_id=space_id)

查询时未加 is_deleted=False,对已软删除的变量再次调用 delete() 会重复执行物理删除(若模型 delete() 是硬删除)或逻辑混乱。应与 update_variable.py 保持一致,加上 is_deleted=False


✨ 改进建议 — create_variable 缺少 result 字段在异常返回中

文件: bkflow/apigw/views/create_variable.py,第 52 行:

return {"code": err_code.ERROR.code, "message": f"创建变量失败: {str(e)}"}

返回体缺少 "result": False,与同文件其他返回格式及项目约定不一致(update_variable.py 同样有此问题)。

# Reviewed, transaction id: 75807
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

本次 PR 新增了变量管理模块(variable_manager),包含 CRUD 接口、API 网关接入和内部接口对接,总体结构清晰、文档齐全。发现以下几个需要关注的问题:


🚨 严重 / ⚠️ 逻辑

1. delete_variable.py — 硬删除与软删除字段不一致
variable.delete() 执行的是 Django 硬删除,但模型已有 is_deleted 软删除字段,且查询条件也用 is_deleted=False 过滤。应改为 variable.is_deleted = True; variable.save() 或调用项目公共的软删除方法,否则硬删除后 is_deleted 过滤逻辑形同虚设。

2. task/operations.pyget_variable() 无错误处理
space_var = InterfaceModuleClient().get_variable(...) 之后直接 space_var["data"],若接口调用失败(网络异常、返回非 200)result=False,此处会 KeyError 或抛出异常导致任务启动失败。应判断 space_var.get("result") 后再取 data

3. variable_manager/views.pyVariableInternalViewSet 基础 queryset 包含已删除记录
queryset = VariableManager.objects.all() 未过滤 is_deleted=False,虽然 get_variable action 内部额外加了过滤,但 AdminModelViewSet 继承的其他默认 action(list/retrieve 等)可能暴露软删除数据。

4. variable_manager/views.pyspace_id 直接 [] 取值无 KeyError 防护
space_id = request.query_params["space_id"] 若调用方未传参会抛出 KeyError,应使用 .get("space_id") 并做非空校验,或用 Serializer 校验 query params。


⚠️ 逻辑

5. update_variable.py — 部分错误响应缺少 result 字段
json.JSONDecodeError 和最外层 except 的返回均缺少 "result": False,与 create_variable.py 的响应格式不一致,调用方无法统一判断。

6. api-resources.ymldelete_variable / update_variable 使用 POST
删除操作用 POST /delete_variable/,更新操作用 POST /update_variable/,不符合 REST 语义(规范要求删除用 DELETE,增量更新用 PATCH)。


✨ 改进

7. constants.pyVARIABLE_TYPE_SPACE 命名易混淆
该常量包含 spacescope 两个值,但名称 VARIABLE_TYPE_SPACE 暗示只有 space 类型,建议改为 VALID_VARIABLE_TYPESVARIABLE_TYPE_CHOICES

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review 总结

本轮对比上一轮评审,大部分问题已得到修复,以下为复核结果:

✅ 已解决的问题

  • get_variable_list HTTP 方法:已从 require_POST 改为 require_GET,REST 语义正确
  • 硬编码类型字符串:已抽取为 VariableType 枚举和 VARIABLE_TYPE_SPACE 常量
  • Space 存在性校验:已从 create() 移至 validate_space_id(),校验时机正确
  • get_variable action is_deleted 过滤:已在查询中加入 is_deleted=False
  • delete_variable 未过滤已删除记录:已加入 is_deleted=False 过滤

🚨 仍需修复的问题

1. operations.pyget_variable() 无错误处理(Critical)

space_var["data"] 直接访问:若接口调用失败(网络异常、result=False),会直接抛出 KeyError 导致任务启动中断且无日志。两处均受影响(start()get_node_data())。

# 建议改为:
if not space_var.get("result"):
    logger.error(f"get space variable failed: {space_var.get('message')}")
    space_var_data = {}
else:
    space_var_data = space_var.get("data", {})
root_pipeline_context.update(space_var_data)

2. delete_variable.py — 硬删除与软删除字段不一致(Important)

模型有 is_deleted 软删除字段,查询也用 is_deleted=False 过滤,但 variable.delete() 执行的是 Django 硬删除,二者语义冲突。已删除的变量不会留下软删除记录,is_deleted 过滤逻辑形同虚设。建议改为:

variable.is_deleted = True
variable.save(update_fields=["is_deleted"])

3. views.pyrequest.query_params["space_id"] 无防护(Important)

VariableInternalViewSet.get_variable 中直接用 [] 取值,调用方未传参时会抛出 KeyError 而非返回 400。建议改为 .get("space_id") 并做非空校验。

4. update_variable.pyJSONDecodeError 响应缺少 result 字段(Minor)

第 52 行 json.JSONDecodeError 的返回体缺少 "result": False,与同文件及项目其他接口的响应格式不一致,调用方无法统一判断。


总体评价:本 PR 变量管理模块结构清晰,文档齐全,迁移文件已同步,本轮修复了多个校验和 REST 规范问题。上述 4 个遗留问题中,operations.py 的错误处理和 delete_variable 的删除语义问题对运行时稳定性影响较大,建议优先处理。

# Reviewed, transaction id: 75816
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review 总结(本轮复核)

对照上一轮评审(commit b7a40001)与当前 HEAD(6a4c7b9)的变更进行复核。


✅ 上轮已确认解决的问题(本轮验证保持已解决)

  • get_variable_list HTTP 方法(POST → GET)
  • 硬编码类型字符串 → VariableType 枚举 + VARIABLE_TYPE_SPACE 常量
  • Space 存在性校验从 create() 移至 validate_space_id()
  • get_variable action 加入 is_deleted=False 过滤
  • delete_variable 加入 is_deleted=False 过滤
  • delete_variable.delete() 软删除语义:CommonModel.delete() 已重写为软删除,✅ 无问题

🚨 仍未解决的问题

1. bkflow/task/operations.py 第 191 行 — space_var["data"] 无错误处理(Critical)

space_var = InterfaceModuleClient().get_variable(self.task_instance.space_id)
root_pipeline_context.update(space_var["data"])  # ← 若 result=False,直接 KeyError

接口调用失败时(网络异常、服务不可用),space_var 中无 "data" 键,会直接抛出 KeyError 导致任务启动中断且无任何业务日志。建议:

if not space_var.get("result"):
    logger.error("get space variable failed: %s", space_var.get("message"))
    space_var_data = {}
else:
    space_var_data = space_var.get("data", {})
root_pipeline_context.update(space_var_data)

2. bkflow/variable_manager/views.py 第 62 行 — request.query_params["space_id"] 无防护(Important)

space_id = request.query_params["space_id"]  # ← 未传参时抛 KeyError,返回 500

调用方未传 space_id 时会返回 500 而非 400。建议改用 .get("space_id") 并做非空校验,或用 Serializer 校验 query params。

3. bkflow/apigw/views/update_variable.py 第 52 行 — JSONDecodeError 响应缺少 result 字段(Minor)

return {"code": err_code.VALIDATION_ERROR.code, "message": "请求体JSON格式错误"}
# ← 缺少 "result": False,与项目响应格式约定不一致

总体评价:本 PR 变量管理模块结构清晰,文档齐全,迁移文件已同步,本轮又解决了多个遗留问题。operations.py 的错误处理问题对运行时稳定性影响最大,建议优先处理。

@codecov-commenter
Copy link

codecov-commenter commented Mar 14, 2026

Codecov Report

❌ Patch coverage is 66.19718% with 72 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (develop@84f4627). Learn more about missing BASE report.

Files with missing lines Patch % Lines
bkflow/apigw/views/update_variable.py 50.00% 15 Missing ⚠️
bkflow/apigw/views/get_variable_list.py 50.00% 14 Missing ⚠️
bkflow/apigw/views/create_variable.py 51.85% 13 Missing ⚠️
bkflow/variable_manager/serializers.py 67.64% 11 Missing ⚠️
bkflow/apigw/views/delete_variable.py 59.09% 9 Missing ⚠️
bkflow/task/operations.py 50.00% 6 Missing ⚠️
bkflow/variable_manager/views.py 87.50% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             develop     #633   +/-   ##
==========================================
  Coverage           ?   83.04%           
==========================================
  Files              ?      262           
  Lines              ?    14720           
  Branches           ?        0           
==========================================
  Hits               ?    12224           
  Misses             ?     2496           
  Partials           ?        0           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

# Reviewed, transaction id: 75817
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review 总结(本轮复核 HEAD: cdca84c

对照上一轮评审(commit 6a4c7b9)与当前 HEAD(cdca84ca)的变更进行复核。


✅ 上轮已解决的问题

  • start()get_variable() 增加了 result 判断与错误日志 ✅
  • delete_variable 软删除语义(CommonModel.delete() 已重写)✅
  • get_variable_list 改为 require_GET
  • Space 存在性校验已移至 validate_space_id()
  • get_variable action 加入 is_deleted=False 过滤 ✅

🚨 仍未解决的问题

1. bkflow/task/operations.pyget_node_data()space_var["data"] 无错误处理(Critical)

start() 已修复,但 get_node_data() 同样的调用仍直接 space_var["data"],接口失败时会 KeyError 导致节点数据获取中断且无业务日志。

# 建议:
if not space_var.get("result"):
    logger.error("get space variable failed: %s", space_var.get("message"))
    space_var_data = {}
else:
    space_var_data = space_var.get("data", {})
root_pipeline_context.update(
    {key: {"type": "plain", "value": value} for key, value in space_var_data.items()}
)

2. bkflow/variable_manager/views.pyrequest.query_params["space_id"] 无防护(Important)

调用方未传 space_id 时抛出 KeyError,返回 500 而非 400。请改用 .get("space_id") 并做非空校验,或用 Serializer 校验 query params。

3. bkflow/variable_manager/views.pyVariableInternalViewSet.queryset 含已删除记录(Important)

queryset = VariableManager.objects.all() 未过滤 is_deleted=FalseAdminModelViewSet 继承的默认 list/retrieve 等 action 可能暴露已软删除数据。建议改为 VariableManager.objects.filter(is_deleted=False)

4. bkflow/apigw/views/update_variable.pyJSONDecodeError 响应缺 result 字段(Minor)

第 52 行返回体 {"code": ..., "message": ...} 缺少 "result": False,与项目其他接口约定不一致,调用方无法统一判断。


总体评价start() 的错误处理已完善,本轮 get_node_data() 存在同样未修复的问题,建议一并处理。VariableInternalViewSet 的 queryset 过滤和 space_id 参数防护问题对线上稳定性影响较大,请优先处理。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants