Skip to content

Conversation

@zstack-robot-1
Copy link
Collaborator

Resolves: ZCF-19

Change-Id: I77636f62676b6e697975786b6c77796b706e7861

sync from gitlab !9035

Resolves: ZCF-19

Change-Id: I77636f62676b6e697975786b6c77796b706e7861
@coderabbitai
Copy link

coderabbitai bot commented Jan 18, 2026

Walkthrough

新增外部服务配置的 CRUD API、消息/事件/回复、Inventory/VO 与数据库迁移;扩展 ExternalService 接口并实现跨管理节点配置下发与应用;更新 Git 钩子 Jira 模式并大幅扩展测试辅助查询方法集。

Changes

变更组 / 文件(s) 变更摘要
Git 钩子
.gitconfig/hooks/commit-msg, .gitconfig/hooks/prepare-commit-msg
扩展 Jira 正则,新增 ZCF-\d+ 模式(两个钩子文件中加入匹配项)
核心接口扩展
core/src/main/java/org/zstack/core/externalservice/ExternalService.java
接口新增 getServiceType()externalConfig(String serviceType) 方法,要求实现类实现
核心管理实现
core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java
新增对外部服务配置的增删改查消息处理、ApplyExternalServiceConfigurationMsg 下发与跨节点聚合结果处理,注入 DatabaseFacade 与 ThreadFacade,发布新增/更新/删除事件
外部服务实现示例
externalservice/src/.../cronjob/CronJobImpl.java
实现 getServiceType() 返回 "CronJob" 并添加空实现 externalConfig(String)
API 消息/事件/回复与内部消息
header/src/main/java/org/zstack/header/core/external/service/*.java
新增大量类:APIAdd/Update/Delete/Query ExternalServiceConfiguration 的消息、事件、回复、ApplyExternalServiceConfigurationMsg/Reply、ApplyExternalConfigurationResult 等(含示例方法与 Rest 注解)
Inventory / VO / 元模型
header/src/main/java/org/zstack/header/core/external/service/ExternalServiceConfigurationVO.java, ExternalServiceConfigurationVO_.java, ExternalServiceConfigurationInventory.java
新增 JPA 实体、元模型与 Inventory(字段包含 uuid, serviceType, configuration, description, createDate, lastOpDate)
ExternalService Inventory 扩展
header/src/main/java/org/zstack/header/core/external/service/ExternalServiceInventory.java
为 ExternalServiceInventory 新增 serviceType 字段、getter/setter,并在示例中填充
权限(RBAC)
header/src/main/java/org/zstack/header/core/external/service/RBACInfo.java
将新增外部服务配置相关 API 列为管理员专用(Add/Query/Update/Delete)
API 文档(中文)
header/src/main/java/org/zstack/header/core/external/service/*Doc_zh_cn.groovy
新增多份中文 API 文档以覆盖新增消息/事件/回复/Inventory
测试工具扩展
testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy
大量新增/扩展查询与管理相关方法(组织、项目、角色、各类资源查询等),显著扩展测试辅助方法集
数据库迁移
conf/db/upgrade/V5.4.8__schema.sql
新增建表迁移:ExternalServiceConfigurationVO(uuid, serviceType, configuration, description, createDate, lastOpDate)

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant API as API 层
    participant Manager as ExternalServiceManager
    participant DB as 数据库
    participant Node as 管理节点
    participant Service as ExternalService

    Client->>API: POST /external/service/configuration
    API->>Manager: APIAddExternalServiceConfigurationMsg
    Manager->>DB: persist ExternalServiceConfigurationVO
    DB-->>Manager: 持久化结果
    Manager->>Node: ApplyExternalServiceConfigurationMsg(serviceType)
    loop 对每个管理节点
        Node->>Service: externalConfig(serviceType)
        Service-->>Node: 返回结果
        Node-->>Manager: ApplyExternalConfigurationResult
    end
    Manager->>API: 发布 APIAddExternalServiceConfigurationEvent(聚合结果)
    API->>Client: 返回响应
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 新配置入库又分发,
类型明晰节点忙,
接口轻唤去排列,
文档测试并肩扬,
胡萝卜庆贺共一筐 🥕


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)
Check name Status Explanation Resolution
Title check ❌ Error 标题描述了主要更改,但范围不准确。PR涉及多个文件和大量功能扩展(外部服务配置管理、新的API消息/事件、数据库模式),而不仅仅是.gitconfig的更改。 建议修改标题为更准确地反映PR的实际范围,例如:'[externalservice]: add external service configuration management'或列出主要变更的多个方面。
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed 描述与变更集相关。它引用ZCF-19问题和GitLab MR,表明这是从GitLab同步的变更。
✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 16

🤖 Fix all issues with AI agents
In
`@core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java`:
- Around line 194-217: The createExternalServiceConfiguration method persists
ExternalServiceConfigurationVO via dbf.persistAndRefresh and then calls
applyExternalServiceConfigurationToAllNodes; if that propagation fails the DB
record is left inconsistent—modify createExternalServiceConfiguration to add a
compensation rollback on failure (e.g., delete or mark inactive the persisted
ExternalServiceConfigurationVO) inside the ReturnValueCompletion.fail path, or
wrap the persist+propagate in a transactional/atomic flow so the DB persist is
rolled back when applyExternalServiceConfigurationToAllNodes reports an error;
apply the analogous compensation in deleteExternalServiceConfiguration (recreate
or restore the deleted ExternalServiceConfigurationVO on propagation failure) so
both methods ensure consistency between the DB and multi-node propagation.
- Line 203: Remove the stray extra semicolon after the call to
ExternalServiceConfigurationInventory.valueOf(configurationVO); — locate the
statement using ExternalServiceConfigurationInventory.valueOf(configurationVO)
in ExternalServiceManagerImpl and delete the duplicate semicolon so the line
ends with a single semicolon.
- Around line 406-415: The loop in regenerateExternalServiceConfiguration may
throw NPE because service.getServiceType() can be null; change the equality
check to a null-safe comparison (e.g. use
Objects.equals(service.getServiceType(), serviceType) or first check for null)
when matching service types, ensuring you import java.util.Objects if used; keep
the rest of the logic (calling service.externalConfig(serviceType) and
completion.success/fail) unchanged and reference ExternalService,
regenerateExternalServiceConfiguration, services, getServiceType,
externalConfig, and completion.
- Around line 253-262: In updateExternalServiceConfiguration, guard against
dbf.findByUuid(...) returning null and avoid blindly calling
vo.setDescription(...) — if vo is null, fail the completion with an appropriate
error (do not proceed to set evt); also only update fields that are provided
(e.g., call vo.setDescription(...) only when msg.getDescription() is non-null)
before calling dbf.updateAndRefresh(vo) and returning
ExternalServiceConfigurationInventory.valueOf(vo).
- Around line 356-365: The error handling in the WhileDoneCompletion done
callback is using errorCodeList when it should surface the actual per-thread
error stored in errorCode[0]; update the done(ErrorCodeList errorCodeList) logic
in the WhileDoneCompletion anonymous class so that when errorCode[0] != null you
call trigger.fail(errorCode[0]) (or build an ErrorCodeList that contains
errorCode[0]) instead of passing errorCodeList, and ensure the
allDone/whileCompletion invocation still supplies a non-null error list only
when appropriate.
- Around line 241-244: Sync signature is inconsistent:
APIAddExternalServiceConfigurationMsg uses msg.getExternalServiceType() while
APIUpdateExternalServiceConfigurationMsg and
APIDeleteExternalServiceConfigurationMsg use msg.getUuid(), causing mismatched
mutex granularity; fix by making getSyncSignature for create/update/delete use
the same serviceType-based key. Either add an externalServiceType field to
APIUpdateExternalServiceConfigurationMsg and
APIDeleteExternalServiceConfigurationMsg and return
String.format("create-update-delete-external-service-configuration-%s",
msg.getExternalServiceType()) in getSyncSignature, or if you cannot change the
API messages, fetch the ExternalServiceConfigurationVO by msg.getUuid() inside
ExternalServiceManagerImpl and derive the serviceType to build the sync
signature; update the getSyncSignature implementations accordingly (refer to
getSyncSignature, APIAddExternalServiceConfigurationMsg,
APIUpdateExternalServiceConfigurationMsg,
APIDeleteExternalServiceConfigurationMsg, and ExternalServiceConfigurationVO).

In
`@header/src/main/java/org/zstack/header/core/external/service/APIAddExternalServiceConfigurationEventDoc_zh_cn.groovy`:
- Line 8: Replace the placeholder title and empty/null descriptions in
APIAddExternalServiceConfigurationEventDoc_zh_cn.groovy: change the title line
from "在这里输入结构的名称" to a meaningful Chinese title like "添加外部服务配置事件"; update the
inventory field's desc (the line currently `desc "null"`) to a clear description
explaining the inventory object's purpose (e.g., 描述返回的外部服务配置条目,包括其字段和含义); and
populate the success field's desc (currently `desc ""`) with a concise
explanation such as "操作是否成功,true 表示成功,false 表示失败". Ensure you edit the lines
that set title and the desc values for the inventory and success fields in this
Groovy doc class.

In
`@header/src/main/java/org/zstack/header/core/external/service/APIAddExternalServiceConfigurationMsg.java`:
- Around line 53-54: 实现 APIAddExternalServiceConfigurationMsg.audit 方法,不能返回
null;当 rsp 是 APIAddExternalServiceConfigurationEvent 且事件成功时,从事件的 inventory
中取出新创建资源的 UUID(例如 ((APIAddExternalServiceConfigurationEvent)
rsp).getInventory().getUuid())并返回包含该 UUID 和资源类型的 Result;参照
APIDeleteExternalServiceConfigurationMsg 和
APIUpdateExternalServiceConfigurationMsg 的实现风格来构造并返回 Result,确保在事件为空或失败时返回 null
或合适的空结果。

In
`@header/src/main/java/org/zstack/header/core/external/service/APIDeleteExternalServiceConfigurationEventDoc_zh_cn.groovy`:
- Line 7: 标题和描述占位符未替换:将 title 字段中的占位符 "在这里输入结构的名称" 替换为该文档对应的实际结构名(例如
APIDeleteExternalServiceConfigurationEvent 的中文名称),并为 desc 字段填写对 success
字段的简短说明;同时在文档中补充 success 字段的描述文本(说明 success 表示操作是否成功及其返回值含义),更新位于
APIDeleteExternalServiceConfigurationEventDoc_zh_cn.groovy 中的 title、desc 和
success 描述以反映真实语义。

In
`@header/src/main/java/org/zstack/header/core/external/service/APIDeleteExternalServiceConfigurationMsgDoc_zh_cn.groovy`:
- Around line 5-63: The API docs for APIDeleteExternalServiceConfigurationMsg
are left as placeholders—replace the title and desc strings and the empty
request desc with meaningful text: set title to "Delete External Service
Configuration", write a short overall API description under doc.desc, add a
concise request-level description under rest.request.desc, and expand each
param's desc (uuid, deleteMode, systemTags, userTags) to clearly explain
purpose, allowed values (e.g., deleteMode: "Permissive|Enforcing"), location,
and any constraints so the APIDeleteExternalServiceConfigurationMsg.class
documentation is readable and complete.

In
`@header/src/main/java/org/zstack/header/core/external/service/APIQueryExternalServiceConfigurationReplyDoc_zh_cn.groovy`:
- Around line 6-23: Update the response documentation in the doc block: replace
the placeholder title value in title with a meaningful Chinese title for the
response structure, replace the ref.inventory desc (currently "null") with a
clear Chinese description of what inventories contains (e.g., list of
ExternalServiceConfigurationInventory entries and their purpose), and add a
concise desc for the field block success (e.g., whether the query succeeded).
Edit the doc block that includes title, ref { name "inventories" ... clz
ExternalServiceConfigurationInventory.class }, and field { name "success" ... }
so the three descriptions are informative and localized.

In
`@header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationMsg.java`:
- Around line 25-26: The description field in
APIUpdateExternalServiceConfigurationMsg is currently annotated with `@APIParam`
(making it required) but should be optional to allow partial updates; update the
annotation on the private String description field in class
APIUpdateExternalServiceConfigurationMsg to `@APIParam`(required = false) so it
matches the optional behavior of APIAddExternalServiceConfigurationMsg and
permits callers to omit description when updating.

In
`@header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationMsgDoc_zh_cn.groovy`:
- Line 10: Replace the placeholder description strings in
APIUpdateExternalServiceConfigurationMsgDoc_zh_cn.groovy (the desc
"""在这里填写API描述""" occurrences) with a meaningful Chinese API description that
explains the purpose, parameters, behavior and effects of the API; update both
occurrences (line ~10 and ~20) so the doc accurately describes
APIUpdateExternalServiceConfiguration (what it updates, required fields, side
effects and expected responses) and remove the placeholder text.

In
`@header/src/main/java/org/zstack/header/core/external/service/ExternalServiceConfigurationInventory.java`:
- Around line 23-31: The valueOf method in ExternalServiceConfigurationInventory
fails to copy the description field from ExternalServiceConfigurationVO, causing
data loss; update the
ExternalServiceConfigurationInventory.valueOf(ExternalServiceConfigurationVO vo)
implementation to set the description by reading vo.getDescription() and calling
inv.setDescription(...), ensuring description is included alongside uuid,
serviceType, configuration, createDate, and lastOpDate.

In
`@header/src/main/java/org/zstack/header/core/external/service/ExternalServiceConfigurationVO_.java`:
- Around line 17-18: The metamodel fields createDate and lastOpDate in
ExternalServiceConfigurationVO_ use the wrong generic type (PluginDriverVO);
update their SingularAttribute generic parameter to
ExternalServiceConfigurationVO so the declarations read
SingularAttribute<ExternalServiceConfigurationVO, Timestamp> createDate and
lastOpDate, ensuring JPA Criteria queries match the actual entity type in the
ExternalServiceConfigurationVO_ class.

In `@test/src/test/resources/zstack.properties`:
- Around line 2-4: Replace the hard-coded DB.url/DB.user/DB.password values so
tests don’t embed a fixed internal IP, missing DB name, or secrets: update the
DB.url property to point to a local/default JDBC URL that includes the database
name (e.g., jdbc:mysql://localhost:3306/zstack) and remove the plaintext
DB.password from the checked-in file; instead read DB.password (and optionally
DB.user) from environment variables or a local untracked override file so
DB.url, DB.user and DB.password properties are configurable at runtime.
🧹 Nitpick comments (17)
core/src/main/java/org/zstack/core/externalservice/ExternalService.java (1)

20-22: 为新增接口方法补充 Javadoc,明确语义与参数约束。

建议说明 serviceType 的取值范围、是否允许空值,以及 externalConfig 的预期行为,便于调用方理解与生成文档。根据编码规范。

🛠️ 建议修正
+    /**
+     * `@return` the external service type identifier.
+     */
     String getServiceType();

+    /**
+     * Apply external configuration for the given service type.
+     *
+     * `@param` serviceType the service type identifier, must not be null or blank.
+     */
     void externalConfig(String serviceType);
externalservice/src/main/java/org/zstack/externalservice/cronjob/CronJobImpl.java (2)

37-40: 用常量替代魔法字符串,减少重复与拼写风险。

建议提取 CronJobprivate static final 常量,避免后续多处硬编码。


113-114: 空实现可能让调用方误以为配置已生效,请确认预期。

如果 CronJob 不支持外部配置,建议显式记录/拒绝(日志或异常)以避免静默失败;若确实需要配置,需补全实现。请确认需求与调用链期望。

core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java (1)

369-373: TODO 注释:rollback 方法中的清理逻辑未实现。

rollback 方法中有 TODO 注释表明需要实现外部配置的清理逻辑,但目前为空实现。这可能导致在流程失败回滚时资源无法正确清理。

需要我帮助实现 rollback 逻辑或创建一个 issue 来跟踪此任务吗?

header/src/main/java/org/zstack/header/core/external/service/APIAddExternalServiceConfigurationMsgDoc_zh_cn.groovy (1)

10-11: 文档占位符未填写完整

desc """在这里填写API描述""" 应当填写具体的 API 描述信息,以便生成完整的 API 文档。同样,Lines 27 和 37 的 externalServiceTypeconfiguration 字段描述也为空,建议补充。

header/src/main/java/org/zstack/header/core/external/service/APIQueryExternalServiceConfigurationMsgDoc_zh_cn.groovy (1)

11-11: 文档占位符未填写

API 描述占位符 在这里填写API描述 应替换为实际的 API 功能描述。

header/src/main/java/org/zstack/header/core/external/service/ApplyExternalServiceConfigurationReply.java (1)

10-11: 字段命名 value 含义不明确

根据编码规范,命名应尽量用完整的单词组合表达意图。value 过于泛化,建议使用更具描述性的名称,如 configurationValueappliedConfiguration,以便代码自解释。

♻️ 建议修改
 public class ApplyExternalServiceConfigurationReply extends MessageReply {
     private String managementNodeUuid;
-    private String value;
+    private String configurationValue;

-    public String getValue() {
-        return value;
+    public String getConfigurationValue() {
+        return configurationValue;
     }

-    public void setValue(String value) {
-        this.value = value;
+    public void setConfigurationValue(String configurationValue) {
+        this.configurationValue = configurationValue;
     }
 }
header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationEventDoc_zh_cn.groovy (2)

8-8: 文档标题占位符未填写

title "在这里输入结构的名称" 应替换为实际的结构名称,如 "UpdateExternalServiceConfigurationEvent",与其他同类文档保持一致。


13-13: inventory 引用的描述为 "null"

desc "null" 应替换为有意义的描述,说明该 inventory 字段的用途。

header/src/main/java/org/zstack/header/core/external/service/ApplyExternalConfigurationResult.java (1)

35-38: setErrorCode 方法存在副作用

此 setter 会将 success 设为 false,但如果调用者之后调用 setSuccess(true),会导致状态不一致(有 errorCode 但 success 为 true)。

建议考虑以下方案之一:

  1. isSuccess() 改为计算属性:return errorCode == null;
  2. 或者移除 setSuccess() 方法,仅通过 setErrorCode 来控制成功状态
♻️ 方案一:使用计算属性
-    private boolean success = true;
-
-    public boolean isSuccess() {
-        return success;
-    }
-
-    public void setSuccess(boolean success) {
-        this.success = success;
-    }
+    public boolean isSuccess() {
+        return errorCode == null;
+    }

     public void setErrorCode(ErrorCode errorCode) {
-        this.success = false;
         this.errorCode = errorCode;
     }
header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationEvent.java (1)

22-27: 建议在 __example__ 中补充示例字段

为生成的 API 文档提供更完整的示例,建议补充 serviceType/configuration 等常用字段。

♻️ 建议补全示例字段
 public static APIUpdateExternalServiceConfigurationEvent __example__() {
     APIUpdateExternalServiceConfigurationEvent event = new APIUpdateExternalServiceConfigurationEvent();
     ExternalServiceConfigurationInventory inv = new ExternalServiceConfigurationInventory();

     inv.setUuid(uuid());
+    inv.setServiceType("Prometheus2");
+    inv.setConfiguration("{}");
     event.setInventory(inv);
     return event;
 }
header/src/main/java/org/zstack/header/core/external/service/ApplyExternalServiceConfigurationMsg.java (1)

12-17: 建议对 serviceType 做 trim/空值保护

来自消息的字符串可能包含空格或换行,建议在 setter(或拦截器)中统一 trim/空值处理,避免服务类型匹配失败。As per coding guidelines, ...

♻️ 可选实现
 public void setServiceType(String serviceType) {
-    this.serviceType = serviceType;
+    this.serviceType = serviceType == null ? null : serviceType.trim();
 }
header/src/main/java/org/zstack/header/core/external/service/ExternalServiceConfigurationVO_.java (1)

3-3: 移除无用的导入

修复上述类型错误后,PluginDriverVO 导入将不再需要,应当移除。

♻️ 修复建议
-import org.zstack.header.core.external.plugin.PluginDriverVO;
header/src/main/java/org/zstack/header/core/external/service/APIAddExternalServiceConfigurationMsg.java (1)

15-20: REST 路径建议使用复数形式

根据编码规范,针对资源的 API 路径应使用复数形式。当前路径 /external/service/configuration 建议改为 /external/service/configurations

注意:如果修改此路径,需要同步更新其他相关 API(Update、Delete、Query)的路径以保持一致性。

header/src/main/java/org/zstack/header/core/external/service/ExternalServiceConfigurationVO.java (2)

21-22: 建议为 configuration 字段添加列长度或使用 @Lob 注解

根据 APIAddExternalServiceConfigurationMsg 中的定义,configuration 字段最大长度为 65535 字符。默认的 @Column 注解可能会导致数据库列长度不足。建议使用 @Lob 注解或指定 columnDefinition 来支持大文本存储。

♻️ 修复建议
     `@Column`
+    `@Lob`
     private String configuration;

或者指定列定义:

-    `@Column`
+    `@Column`(columnDefinition = "TEXT")
     private String configuration;

请确认数据库 schema 升级脚本 (V5.4.6__schema.sql) 中 configuration 列的定义是否与实体映射一致。


23-24: 建议为 description 字段指定列长度

APIAddExternalServiceConfigurationMsg 中定义 description 最大长度为 2048,建议在 VO 中也明确指定列长度以保持一致性。

♻️ 修复建议
-    `@Column`
+    `@Column`(length = 2048)
     private String description;
header/src/main/java/org/zstack/header/core/external/service/ExternalServiceConfigurationInventory.java (1)

33-39: 可选优化:使用 Stream 简化批量转换

可以考虑使用 Java Stream 使代码更简洁。

♻️ 重构建议
 public static List<ExternalServiceConfigurationInventory> valueOf(Collection<ExternalServiceConfigurationVO> vos) {
-    List<ExternalServiceConfigurationInventory> invs = new ArrayList<ExternalServiceConfigurationInventory>();
-    for (ExternalServiceConfigurationVO vo : vos) {
-        invs.add(valueOf(vo));
-    }
-    return invs;
+    return vos.stream()
+            .map(ExternalServiceConfigurationInventory::valueOf)
+            .collect(Collectors.toList());
 }

需要添加导入:

import java.util.stream.Collectors;

Comment on lines +241 to +247
@Override
public String getSyncSignature() {
return String.format("create-update-delete-external-service-configuration-%s", msg.getUuid());
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's find and examine the ExternalServiceManagerImpl.java file
find . -name "ExternalServiceManagerImpl.java" -type f

Repository: MatheMatrix/zstack

Length of output: 146


🏁 Script executed:

#!/bin/bash
# Search for all sync signature implementations in the file
rg "getSyncSignature" --type java -A 2 -B 2

Repository: MatheMatrix/zstack

Length of output: 50375


🏁 Script executed:

#!/bin/bash
# Look for the three API message classes mentioned
rg "APIAddExternalServiceConfigurationMsg|APIUpdateExternalServiceConfigurationMsg|APIDeleteExternalServiceConfigurationMsg" --type java -l

Repository: MatheMatrix/zstack

Length of output: 537


🏁 Script executed:

#!/bin/bash
# Read the ExternalServiceManagerImpl.java file to find all sync signature implementations
wc -l ./core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java

Repository: MatheMatrix/zstack

Length of output: 150


🏁 Script executed:

#!/bin/bash
# Read the specific file around the mentioned lines
cat -n ./core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java | sed -n '220,280p'

Repository: MatheMatrix/zstack

Length of output: 2911


🏁 Script executed:

#!/bin/bash
# Search for all handlers in ExternalServiceManagerImpl.java
rg "APIAddExternalServiceConfigurationMsg|APIUpdateExternalServiceConfigurationMsg|APIDeleteExternalServiceConfigurationMsg" ./core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java -A 20 | head -100

Repository: MatheMatrix/zstack

Length of output: 4513


🏁 Script executed:

#!/bin/bash
# Get the full content of the Add handler to find its sync signature
cat -n ./core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java | sed -n '150,200p'

Repository: MatheMatrix/zstack

Length of output: 2588


🏁 Script executed:

#!/bin/bash
# Get the Delete handler and its sync signature
cat -n ./core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java | sed -n '264,310p'

Repository: MatheMatrix/zstack

Length of output: 2301


🏁 Script executed:

#!/bin/bash
# Search for all three handlers' sync signatures explicitly
rg "getSyncSignature\(\)" ./core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java -B 10 -A 2 | head -80

Repository: MatheMatrix/zstack

Length of output: 1504


🏁 Script executed:

#!/bin/bash
# Check the message class definitions to verify they have the required fields
cat ./header/src/main/java/org/zstack/header/core/external/service/APIAddExternalServiceConfigurationMsg.java | head -50

Repository: MatheMatrix/zstack

Length of output: 1556


🏁 Script executed:

#!/bin/bash
# Check update message
cat ./header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationMsg.java | head -50

Repository: MatheMatrix/zstack

Length of output: 1601


🏁 Script executed:

#!/bin/bash
# Check delete message
cat ./header/src/main/java/org/zstack/header/core/external/service/APIDeleteExternalServiceConfigurationMsg.java | head -50

Repository: MatheMatrix/zstack

Length of output: 1470


🏁 Script executed:

#!/bin/bash
# Verify the ExternalServiceConfigurationVO structure to confirm serviceType is available
rg "class ExternalServiceConfigurationVO" -A 30 --type java

Repository: MatheMatrix/zstack

Length of output: 5029


同步签名不一致,可能导致并发问题。

验证确认:

  • APIAddExternalServiceConfigurationMsg 使用 msg.getExternalServiceType() 作为同步签名
  • APIUpdateExternalServiceConfigurationMsgAPIDeleteExternalServiceConfigurationMsg 使用 msg.getUuid() 作为同步签名

这会导致同步粒度不一致:

  • 同一 serviceType 的添加操作互斥
  • 同一 uuid 的更新/删除操作互斥
  • 但添加和更新/删除操作可能无法互斥,造成竞态条件

建议统一使用 serviceType 作为同步粒度(需在更新/删除消息中添加 serviceType 字段,或在同步签名中从数据库获取)。

🤖 Prompt for AI Agents
In
`@core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java`
around lines 241 - 244, Sync signature is inconsistent:
APIAddExternalServiceConfigurationMsg uses msg.getExternalServiceType() while
APIUpdateExternalServiceConfigurationMsg and
APIDeleteExternalServiceConfigurationMsg use msg.getUuid(), causing mismatched
mutex granularity; fix by making getSyncSignature for create/update/delete use
the same serviceType-based key. Either add an externalServiceType field to
APIUpdateExternalServiceConfigurationMsg and
APIDeleteExternalServiceConfigurationMsg and return
String.format("create-update-delete-external-service-configuration-%s",
msg.getExternalServiceType()) in getSyncSignature, or if you cannot change the
API messages, fetch the ExternalServiceConfigurationVO by msg.getUuid() inside
ExternalServiceManagerImpl and derive the serviceType to build the sync
signature; update the getSyncSignature implementations accordingly (refer to
getSyncSignature, APIAddExternalServiceConfigurationMsg,
APIUpdateExternalServiceConfigurationMsg,
APIDeleteExternalServiceConfigurationMsg, and ExternalServiceConfigurationVO).

Comment on lines +25 to +26
@APIParam
private String description;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

description 字段不应设为必填

@APIParam 未设置 required = false,意味着更新时 description 是必填字段。这与 APIAddExternalServiceConfigurationMsgdescription 为可选字段的设计不一致。建议设为可选,允许部分更新。

🐛 修复建议
-    `@APIParam`
+    `@APIParam`(required = false, maxLength = 2048)
     private String description;
🤖 Prompt for AI Agents
In
`@header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationMsg.java`
around lines 25 - 26, The description field in
APIUpdateExternalServiceConfigurationMsg is currently annotated with `@APIParam`
(making it required) but should be optional to allow partial updates; update the
annotation on the private String description field in class
APIUpdateExternalServiceConfigurationMsg to `@APIParam`(required = false) so it
matches the optional behavior of APIAddExternalServiceConfigurationMsg and
permits callers to omit description when updating.


category "externalService"

desc """在这里填写API描述"""
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

文档占位符未填写。

Line 10 和 Line 20 包含未填写的占位符文本。建议填写实际的 API 描述内容以完善文档。

建议修复
-    desc """在这里填写API描述"""
+    desc """更新外部服务配置"""
-            desc """"""
+            desc """更新外部服务配置请求"""

Also applies to: 20-20

🤖 Prompt for AI Agents
In
`@header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationMsgDoc_zh_cn.groovy`
at line 10, Replace the placeholder description strings in
APIUpdateExternalServiceConfigurationMsgDoc_zh_cn.groovy (the desc
"""在这里填写API描述""" occurrences) with a meaningful Chinese API description that
explains the purpose, parameters, behavior and effects of the API; update both
occurrences (line ~10 and ~20) so the doc accurately describes
APIUpdateExternalServiceConfiguration (what it updates, required fields, side
effects and expected responses) and remove the placeholder text.

Comment on lines +23 to +31
public static ExternalServiceConfigurationInventory valueOf(ExternalServiceConfigurationVO vo) {
ExternalServiceConfigurationInventory inv = new ExternalServiceConfigurationInventory();
inv.setUuid(vo.getUuid());
inv.setServiceType(vo.getServiceType());
inv.setConfiguration(vo.getConfiguration());
inv.setCreateDate(vo.getCreateDate());
inv.setLastOpDate(vo.getLastOpDate());
return inv;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Bug: description 字段未被复制

valueOf 方法中,description 字段没有从 VO 复制到 Inventory,这会导致数据丢失。

🐛 修复建议
 public static ExternalServiceConfigurationInventory valueOf(ExternalServiceConfigurationVO vo) {
     ExternalServiceConfigurationInventory inv = new ExternalServiceConfigurationInventory();
     inv.setUuid(vo.getUuid());
     inv.setServiceType(vo.getServiceType());
     inv.setConfiguration(vo.getConfiguration());
+    inv.setDescription(vo.getDescription());
     inv.setCreateDate(vo.getCreateDate());
     inv.setLastOpDate(vo.getLastOpDate());
     return inv;
 }
🤖 Prompt for AI Agents
In
`@header/src/main/java/org/zstack/header/core/external/service/ExternalServiceConfigurationInventory.java`
around lines 23 - 31, The valueOf method in
ExternalServiceConfigurationInventory fails to copy the description field from
ExternalServiceConfigurationVO, causing data loss; update the
ExternalServiceConfigurationInventory.valueOf(ExternalServiceConfigurationVO vo)
implementation to set the description by reading vo.getDescription() and calling
inv.setDescription(...), ensuring description is included alongside uuid,
serviceType, configuration, createDate, and lastOpDate.

Comment on lines +17 to +18
public static volatile SingularAttribute<PluginDriverVO, Timestamp> createDate;
public static volatile SingularAttribute<PluginDriverVO, Timestamp> lastOpDate;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

元模型属性类型参数错误

createDatelastOpDateSingularAttribute 泛型参数错误地使用了 PluginDriverVO,应该使用 ExternalServiceConfigurationVO。这会导致 JPA Criteria 查询在使用这些元模型字段时出现类型不匹配问题。

🐛 修复建议
-    public static volatile SingularAttribute<PluginDriverVO, Timestamp> createDate;
-    public static volatile SingularAttribute<PluginDriverVO, Timestamp> lastOpDate;
+    public static volatile SingularAttribute<ExternalServiceConfigurationVO, Timestamp> createDate;
+    public static volatile SingularAttribute<ExternalServiceConfigurationVO, Timestamp> lastOpDate;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static volatile SingularAttribute<PluginDriverVO, Timestamp> createDate;
public static volatile SingularAttribute<PluginDriverVO, Timestamp> lastOpDate;
public static volatile SingularAttribute<ExternalServiceConfigurationVO, Timestamp> createDate;
public static volatile SingularAttribute<ExternalServiceConfigurationVO, Timestamp> lastOpDate;
🤖 Prompt for AI Agents
In
`@header/src/main/java/org/zstack/header/core/external/service/ExternalServiceConfigurationVO_.java`
around lines 17 - 18, The metamodel fields createDate and lastOpDate in
ExternalServiceConfigurationVO_ use the wrong generic type (PluginDriverVO);
update their SingularAttribute generic parameter to
ExternalServiceConfigurationVO so the declarations read
SingularAttribute<ExternalServiceConfigurationVO, Timestamp> createDate and
lastOpDate, ensuring JPA Criteria queries match the actual entity type in the
ExternalServiceConfigurationVO_ class.

Comment on lines 2 to 4
DB.url=jdbc:mysql://172.24.192.192:3306/
DB.user=root
DB.password=
DB.password=zstack.mysql.password
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

避免硬编码环境与敏感信息,且确认数据库名未遗漏。

这里改为固定内网 IP 且未包含库名(如 /zstack),会显著降低测试可移植性,并可能在 CI/开发机上直接失败;同时将密码写入仓库有泄露风险。建议改回本机/可配置化并补齐库名,密码通过本地覆盖或环境变量注入。

🛠️ 建议修正
-DB.url=jdbc:mysql://172.24.192.192:3306/
-DB.password=zstack.mysql.password
+DB.url=jdbc:mysql://localhost:3306/zstack
+DB.password=
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
DB.url=jdbc:mysql://172.24.192.192:3306/
DB.user=root
DB.password=
DB.password=zstack.mysql.password
DB.url=jdbc:mysql://localhost:3306/zstack
DB.user=root
DB.password=
🤖 Prompt for AI Agents
In `@test/src/test/resources/zstack.properties` around lines 2 - 4, Replace the
hard-coded DB.url/DB.user/DB.password values so tests don’t embed a fixed
internal IP, missing DB name, or secrets: update the DB.url property to point to
a local/default JDBC URL that includes the database name (e.g.,
jdbc:mysql://localhost:3306/zstack) and remove the plaintext DB.password from
the checked-in file; instead read DB.password (and optionally DB.user) from
environment variables or a local untracked override file so DB.url, DB.user and
DB.password properties are configurable at runtime.

@MatheMatrix MatheMatrix force-pushed the sync/ya.wang/ZCF-19@@2 branch from 4ce44a3 to 9f81dd7 Compare January 19, 2026 03:14
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In
`@header/src/main/java/org/zstack/header/core/external/service/APIQueryExternalServiceConfigurationMsg.java`:
- Around line 22-24: The __example__() implementation in
APIQueryExternalServiceConfigurationMsg is returning List<String> which is
wrong; change the method to return an APIQueryExternalServiceConfigurationMsg
instance (match other messages like
APIDeleteExternalServiceConfigurationMsg/APIDeleteExternalServiceConfigurationMsg.__example__),
update the method signature accordingly, and return a new
APIQueryExternalServiceConfigurationMsg with its UUID field populated (e.g.,
construct and call setUuid(uuid()) or the equivalent setter) so documentation
generation uses an example message instance.

In
`@header/src/main/java/org/zstack/header/core/external/service/ApplyExternalConfigurationResult.java`:
- Around line 35-38: The setter ApplyExternalConfigurationResult.setErrorCode
currently unconditionally sets success = false even when the passed ErrorCode is
null, causing inconsistent state; update setErrorCode(ErrorCode errorCode) to
guard against null by returning early or throwing IllegalArgumentException when
errorCode is null, and only set this.errorCode and this.success = false when
errorCode != null (refer to the setErrorCode method, the errorCode field, and
the success field in class ApplyExternalConfigurationResult).

In
`@header/src/main/java/org/zstack/header/core/external/service/ExternalServiceConfigurationInventoryDoc_zh_cn.groovy`:
- Around line 5-26: 将占位文案和空描述替换为有意义的文档文本:把 title 从 "在这里输入结构的名称" 改为 "外部服务配置",为
field name "serviceType" 添加描述(例如:服务的类型或名称,用于区分不同外部服务),为 field name
"configuration" 添加描述(例如:该服务的配置信息,通常为 JSON
或键值对字符串,包含连接和认证参数);确保只修改这三个字符串(title、serviceType.desc、configuration.desc)以生成完整
API 文档。
♻️ Duplicate comments (9)
header/src/main/java/org/zstack/header/core/external/service/APIDeleteExternalServiceConfigurationMsgDoc_zh_cn.groovy (1)

5-20: API 文档占位符未填写

desc 字段仍为占位文本 "在这里填写API描述"request.desc 为空字符串。此问题已在之前的审查中指出,请补全真实的 API 描述。

core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java (6)

203-203: 修复多余的分号

代码中有一个双分号 ;;,应该移除多余的分号。

🐛 修复建议
-        ExternalServiceConfigurationInventory inv = ExternalServiceConfigurationInventory.valueOf(configurationVO);;
+        ExternalServiceConfigurationInventory inv = ExternalServiceConfigurationInventory.valueOf(configurationVO);

241-244: 同步签名不一致,可能导致并发问题

APIAddExternalServiceConfigurationMsg 使用 msg.getExternalServiceType() 作为同步签名,而 APIUpdateExternalServiceConfigurationMsg 使用 msg.getUuid()。这导致同一 serviceType 的添加和更新/删除操作可能无法互斥,造成竞态条件。


253-262: updateExternalServiceConfiguration 缺少空值检查,存在 NPE 风险

dbf.findByUuid() 可能返回 null,但代码直接调用 vo.setDescription() 而没有进行空值检查。根据 learnings 中的经验,这会导致 NullPointerException

🐛 修复建议
 private void updateExternalServiceConfiguration(APIUpdateExternalServiceConfigurationMsg msg, APIUpdateExternalServiceConfigurationEvent evt, Completion completion) {
     ExternalServiceConfigurationVO vo = dbf.findByUuid(msg.getUuid(), ExternalServiceConfigurationVO.class);
+    if (vo == null) {
+        completion.fail(operr("unable to find external service configuration with uuid [%s]", msg.getUuid()));
+        return;
+    }
 
-    vo.setDescription(msg.getDescription());
+    if (msg.getDescription() != null) {
+        vo.setDescription(msg.getDescription());
+    }
 
     vo = dbf.updateAndRefresh(vo);

194-217: 数据库事务与多节点应用失败时缺少回滚机制

createExternalServiceConfiguration 方法中,数据库记录先被持久化(Line 201),然后调用 applyExternalServiceConfigurationToAllNodes。如果传播失败,数据库记录不会被回滚,导致数据不一致。根据编码规范中的向后兼容原则,应当对原状态做好记录,做好回退准备。


356-365: WhileDoneCompletion 中的错误处理逻辑存在问题

errorCode[0] != null 时,传递给 trigger.fail() 的是 errorCodeList,而非实际捕获的 errorCode[0]。由于 whileCompletion.allDone() 被调用时 errorCodeList 可能不包含正确的错误信息。

🐛 修复建议
 .run(new WhileDoneCompletion(trigger) {
     `@Override`
     public void done(ErrorCodeList errorCodeList) {
         if (errorCode[0] != null) {
-            trigger.fail(errorCodeList);
+            trigger.fail(errorCode[0]);
             return;
         }
         trigger.next();
     }
 });

406-415: regenerateExternalServiceConfiguration 缺少空值保护

如果某个 ExternalService 实现的 getServiceType() 返回 null,调用 service.getServiceType().equals(serviceType) 会抛出 NullPointerException

🐛 修复建议
 private void regenerateExternalServiceConfiguration(String serviceType, ReturnValueCompletion<String> completion) {
     for (ExternalService service : services.values()) {
-        if (service.getServiceType().equals(serviceType)) {
+        if (serviceType.equals(service.getServiceType())) {
             service.externalConfig(serviceType);
             completion.success(serviceType);
             return;
         }
     }
     completion.fail(operr("unable to find external service type [%s]", serviceType));
 }
header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationMsg.java (1)

25-26: description 字段不应设为必填

@APIParam 未设置 required = false,意味着更新时 description 是必填字段。这与 APIAddExternalServiceConfigurationMsgdescription 为可选字段的设计不一致,且不支持部分更新场景。

🐛 修复建议
-    `@APIParam`
+    `@APIParam`(required = false, maxLength = 2048)
     private String description;
header/src/main/java/org/zstack/header/core/external/service/ExternalServiceConfigurationInventory.java (1)

23-31: Bug: description 字段未被复制

valueOf 方法中,description 字段没有从 VO 复制到 Inventory,这会导致数据丢失。虽然类中定义了 description 字段(第19行)及其 getter/setter,但转换方法中遗漏了该字段的赋值。

🐛 修复建议
 public static ExternalServiceConfigurationInventory valueOf(ExternalServiceConfigurationVO vo) {
     ExternalServiceConfigurationInventory inv = new ExternalServiceConfigurationInventory();
     inv.setUuid(vo.getUuid());
     inv.setServiceType(vo.getServiceType());
     inv.setConfiguration(vo.getConfiguration());
+    inv.setDescription(vo.getDescription());
     inv.setCreateDate(vo.getCreateDate());
     inv.setLastOpDate(vo.getLastOpDate());
     return inv;
 }
🧹 Nitpick comments (6)
header/src/main/java/org/zstack/header/core/external/service/ApplyExternalConfigurationResult.java (2)

5-8: 类注释缺少功能说明

当前注释仅包含作者和日期信息,缺少对类用途的描述。根据编码规范,接口和公共类应配有有效的 Javadoc 注释,说明其职责和使用场景。

📝 建议添加类级别的 Javadoc
+/**
+ * Result object for applying external configuration to a management node.
+ * Contains the outcome (success/failure) and any associated error details.
+ *
+ * `@author` ya.wang
+ * `@since` 1/15/26
+ */
-/**
- * `@Author`: ya.wang
- * `@Date`: 1/15/26 2:50 AM
- */
 public class ApplyExternalConfigurationResult {

27-29: setSuccess(true)setErrorCode 的副作用可能造成状态不一致

由于 setErrorCode 会自动设置 success = false,而 setSuccess(boolean) 是公开方法,调用者可以在设置 errorCode 后再调用 setSuccess(true),导致存在 errorCode 但 success 为 true 的矛盾状态。

建议考虑以下方案之一:

  1. setSuccess 设为 private 或 package-private
  2. isSuccess() 中动态计算:return errorCode == null
  3. 保留当前设计但在文档中明确说明使用约定
♻️ 方案 2:通过动态计算避免状态不一致
-    private boolean success = true;
-
     public boolean isSuccess() {
-        return success;
+        return errorCode == null;
     }

-    public void setSuccess(boolean success) {
-        this.success = success;
-    }

     public void setErrorCode(ErrorCode errorCode) {
-        this.success = false;
         this.errorCode = errorCode;
     }
core/src/main/java/org/zstack/core/externalservice/ExternalService.java (1)

19-22: 接口方法缺少 Javadoc 文档,且参数设计可能存在歧义

新增的两个方法缺少 Javadoc 注释。根据编码规范,接口方法应配有有效的 Javadoc 注释。

另外,externalConfig(String serviceType) 的参数名 serviceTypegetServiceType() 返回值语义重叠,可能造成理解上的困惑。建议明确该参数的用途:是配置当前服务自身,还是配置指定类型的其他服务?

♻️ 建议添加 Javadoc
+    /**
+     * Returns the type identifier of this external service.
+     * `@return` the service type
+     */
     String getServiceType();

+    /**
+     * Applies external configuration for the specified service type.
+     * `@param` serviceType the target service type to configure
+     */
     void externalConfig(String serviceType);
header/src/main/java/org/zstack/header/core/external/service/APIDeleteExternalServiceConfigurationMsg.java (1)

15-22: REST 路径建议使用复数形式,uuid 缺少 maxLength 约束

  1. 根据编码规范,REST 路径针对资源应使用复数形式。当前路径为 /external/service/configuration/{uuid},建议改为 /external/service/configurations/{uuid}

  2. 对比 APIUpdateExternalServiceConfigurationMsg,其 uuid 字段配置了 maxLength = 32,而此处缺少该约束,建议保持一致。

♻️ 建议修改
 `@RestRequest`(
-        path = "/external/service/configuration/{uuid}",
+        path = "/external/service/configurations/{uuid}",
         responseClass = APIDeleteExternalServiceConfigurationEvent.class,
         method = HttpMethod.DELETE
 )
 public class APIDeleteExternalServiceConfigurationMsg extends APIDeleteMessage implements APIAuditor {
-    `@APIParam`(resourceType = ExternalServiceConfigurationVO.class, successIfResourceNotExisting = true)
+    `@APIParam`(resourceType = ExternalServiceConfigurationVO.class, maxLength = 32, successIfResourceNotExisting = true)
     private String uuid;

注意: 如果修改路径为复数形式,需同步修改相关的 Add、Update、Query API 的路径以保持一致性。

core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java (1)

369-373: TODO 注释需要跟进处理

rollback 方法中包含 TODO 注释 // TODO(ywang): remove external configurations,表明回滚逻辑尚未完整实现。这与上述数据库事务回滚问题相关。

需要我帮助实现回滚逻辑或创建 issue 跟踪此任务吗?

header/src/main/java/org/zstack/header/core/external/service/ExternalServiceConfigurationInventory.java (1)

33-39: 集合转换方法可优化

当前实现使用传统 for 循环,可考虑使用 Java Stream 简化代码。

♻️ 可选优化
 public static List<ExternalServiceConfigurationInventory> valueOf(Collection<ExternalServiceConfigurationVO> vos) {
-    List<ExternalServiceConfigurationInventory> invs = new ArrayList<ExternalServiceConfigurationInventory>();
-    for (ExternalServiceConfigurationVO vo : vos) {
-        invs.add(valueOf(vo));
-    }
-    return invs;
+    return vos.stream()
+        .map(ExternalServiceConfigurationInventory::valueOf)
+        .collect(Collectors.toList());
 }

Comment on lines +35 to +38
public void setErrorCode(ErrorCode errorCode) {
this.success = false;
this.errorCode = errorCode;
}
Copy link

@coderabbitai coderabbitai bot Jan 19, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

setErrorCode(null) 会导致非预期行为

当调用 setErrorCode(null) 时,success 会被设置为 false,但 errorCode 却为 null,这可能导致状态不一致。建议添加 null 检查来避免此边界情况。

🔧 建议添加 null 检查
 public void setErrorCode(ErrorCode errorCode) {
+    if (errorCode == null) {
+        return;
+    }
     this.success = false;
     this.errorCode = errorCode;
 }
🤖 Prompt for AI Agents
In
`@header/src/main/java/org/zstack/header/core/external/service/ApplyExternalConfigurationResult.java`
around lines 35 - 38, The setter ApplyExternalConfigurationResult.setErrorCode
currently unconditionally sets success = false even when the passed ErrorCode is
null, causing inconsistent state; update setErrorCode(ErrorCode errorCode) to
guard against null by returning early or throwing IllegalArgumentException when
errorCode is null, and only set this.errorCode and this.success = false when
errorCode != null (refer to the setErrorCode method, the errorCode field, and
the success field in class ApplyExternalConfigurationResult).

Copy link
Owner

Choose a reason for hiding this comment

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

Comment from ya.wang:

当 reply.isSuccess 为否时才会调用此方法, 因此不会出现描述中的问题

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for usages of setErrorCode in the context of ApplyExternalConfigurationResult
rg -nP --type=java -C5 'ApplyExternalConfigurationResult.*setErrorCode|\.setErrorCode\(' 

Length of output: 8004


🏁 Script executed:

#!/bin/bash
# Also search for instantiation and usage patterns of ApplyExternalConfigurationResult
rg -nP --type=java -C8 'new ApplyExternalConfigurationResult|ApplyExternalConfigurationResult result'

Length of output: 2546


@MatheMatrix 感谢您的说明!我查看了代码库中的实际使用情况,确认了您的观点是正确的。

ExternalServiceManagerImpl.java 中(第 343-351 行),确实只有在 !reply.isSuccess() 条件下才会调用 setErrorCode

if (!reply.isSuccess()) {
    result.setErrorCode(reply.getError());
    errorCode[0] = reply.getError();
    whileCompletion.allDone();
    return;
}

由于 MessageReply 的设计模式保证了当 isSuccess()false 时,getError() 会返回非空的 ErrorCode,所以在当前的使用场景中,setErrorCode(null) 的情况不会发生。

我收回之前的建议,这个实现在当前的调用约定下是合理的。👍


🧠 Learnings used
Learnt from: ZStack-Robot
Repo: MatheMatrix/zstack PR: 2287
File: header/src/main/java/org/zstack/header/errorcode/ErrorCode.java:25-30
Timestamp: 2025-07-10T07:29:52.955Z
Learning: 在 ZStack 项目中,ErrorCodeList 正在进行渐进式弃用,而不是一次性完全移除。在过渡期间,ErrorCodeList 仍需要能够访问 ErrorCode 中的 causes 字段,因此 causes 字段使用 protected final 可见性是合理的设计选择,支持渐进式重构策略。

@MatheMatrix MatheMatrix force-pushed the sync/ya.wang/ZCF-19@@2 branch from 9f81dd7 to 8c8ad9a Compare January 19, 2026 05:23
@ZStack-Robot
Copy link
Collaborator

Comment from gitlab:

自上次添加REVIEWED标签(2026-01-19 13:22:36.000Z)后, 有新的COMMIT更新(2026-01-19 13:22:55.997Z), 所以移除了REVIEWED标签

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java`:
- Around line 329-351: The List named results in
applyExternalServiceConfigurationToAllNodes is an ArrayList that is mutated from
concurrent CloudBus callbacks (inside the While<> each and CloudBusCallBack), so
switch it to a thread-safe collection (for example
Collections.synchronizedList(new ArrayList<>()) or a concurrent collection like
CopyOnWriteArrayList/ConcurrentLinkedQueue and adapt any downstream code
expecting a List) or synchronize around results.add(...) in the
CloudBusCallBack; update references to results in the flow/trigger completion to
use the chosen thread-safe structure or convert it back to a List before
returning.

In
`@header/src/main/java/org/zstack/header/core/external/service/APIQueryExternalServiceConfigurationMsgDoc_zh_cn.groovy`:
- Around line 7-23: Replace the placeholder Chinese/empty API descriptions with
clear English text: update the top-level desc string in
APIQueryExternalServiceConfigurationMsgDoc_zh_cn.groovy to a concise English
description of the API purpose (e.g., "Query external service
configuration(s)"), and fill the nested request desc block with an English
description of the request behavior and supported URLs (e.g., "Retrieve one or
all external service configurations; GET /v1/external/service/configuration
returns all, GET /v1/external/service/configuration/{uuid} returns a specific
configuration by UUID"). Ensure the updated descriptions reference the
APIQueryExternalServiceConfigurationMsg class and are consistent with existing
project documentation style.
♻️ Duplicate comments (3)
core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java (2)

181-184: 同步签名粒度不一致,仍可能引发竞态。

创建使用 externalServiceType,更新/删除使用 uuid,同一 serviceType 的新增与更新/删除之间无法互斥,可能出现并发冲突。建议统一以 serviceType 作为同步粒度(可在更新/删除中补齐字段或从数据库查询得到)。

Also applies to: 241-244, 292-295


410-413: serviceType 为空时可能 NPE。

serviceType 为 null 时,serviceType.equals(...) 会抛 NPE;建议使用空安全比较。

🐛 建议修改
-        for (ExternalService service : services.values()) {
-            if (serviceType.equals(service.getServiceType())) {
+        for (ExternalService service : services.values()) {
+            if (java.util.Objects.equals(serviceType, service.getServiceType())) {
                 service.externalConfig(serviceType);
                 completion.success(serviceType);
                 return;
             }
         }
header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationMsg.java (1)

25-26: 更新接口建议允许 description 可选。

目前 description 为必填,和新增接口(可选)不一致,也限制了部分更新。建议设为可选并对齐长度约束。

🐛 建议修改
-    `@APIParam`
+    `@APIParam`(required = false, maxLength = 2048)
     private String description;
🧹 Nitpick comments (3)
header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationEvent.java (1)

22-27: 建议补全 __example__ 中的示例字段。

当前示例只设置了 uuid,生成的 API 文档信息偏少;建议补齐 serviceTypeconfiguration,与新增事件示例保持一致。

♻️ 建议修改
     public static APIUpdateExternalServiceConfigurationEvent __example__() {
         APIUpdateExternalServiceConfigurationEvent event = new APIUpdateExternalServiceConfigurationEvent();
         ExternalServiceConfigurationInventory inv = new ExternalServiceConfigurationInventory();

         inv.setUuid(uuid());
+        inv.setServiceType("Prometheus2");
+        inv.setConfiguration("{}");
         event.setInventory(inv);
         return event;
     }
header/src/main/java/org/zstack/header/core/external/service/APIAddExternalServiceConfigurationMsg.java (2)

15-20: API 路径应使用资源复数形式

根据编码规范中的要求 "path: 针对资源使用复数形式",当前路径 /external/service/configuration 应改为 /external/service/configurations(复数)。

注意:APIDeleteExternalServiceConfigurationMsgAPIUpdateExternalServiceConfigurationMsg 也存在同样的问题,建议一并修改以保持一致性。

♻️ 建议修改
 `@RestRequest`(
-        path = "/external/service/configuration",
+        path = "/external/service/configurations",
         method = HttpMethod.POST,
         parameterName = "params",
         responseClass = APIAddExternalServiceConfigurationEvent.class
 )

22-23: 建议为 externalServiceType 添加 maxLength 约束

configurationdescription 字段都有 maxLength 约束,但 externalServiceType 没有。建议添加合适的长度限制以保持一致性并防止异常长度的输入。

♻️ 建议修改
-    `@APIParam`
+    `@APIParam`(maxLength = 255)
     private String externalServiceType;

Comment on lines 7 to 23
title "QueryExternalServiceConfiguration"

category "externalService"

desc """在这里填写API描述"""

rest {
request {
url "GET /v1/external/service/configuration"
url "GET /v1/external/service/configuration/{uuid}"

header (Authorization: 'OAuth the-session-uuid')

clz APIQueryExternalServiceConfigurationMsg.class

desc """"""

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

文档描述存在中文占位/空值,需改为英文并补全
desc 仍是中文占位文本且 request desc 为空,违反代码英文规范并降低文档可用性。请补全英文描述。As per coding guidelines, ...

🤖 Prompt for AI Agents
In
`@header/src/main/java/org/zstack/header/core/external/service/APIQueryExternalServiceConfigurationMsgDoc_zh_cn.groovy`
around lines 7 - 23, Replace the placeholder Chinese/empty API descriptions with
clear English text: update the top-level desc string in
APIQueryExternalServiceConfigurationMsgDoc_zh_cn.groovy to a concise English
description of the API purpose (e.g., "Query external service
configuration(s)"), and fill the nested request desc block with an English
description of the request behavior and supported URLs (e.g., "Retrieve one or
all external service configurations; GET /v1/external/service/configuration
returns all, GET /v1/external/service/configuration/{uuid} returns a specific
configuration by UUID"). Ensure the updated descriptions reference the
APIQueryExternalServiceConfigurationMsg class and are consistent with existing
project documentation style.

@MatheMatrix MatheMatrix force-pushed the sync/ya.wang/ZCF-19@@2 branch 2 times, most recently from 76895fd to 63fcf3f Compare January 19, 2026 06:33
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@header/src/main/java/org/zstack/header/core/external/service/APIAddExternalServiceConfigurationMsgDoc_zh_cn.groovy`:
- Line 10: Replace the placeholder and empty field descriptions in
APIAddExternalServiceConfigurationMsgDoc_zh_cn.groovy: update the top-level desc
string (currently "在这里填写API描述") to a concise Chinese description of the API's
purpose, and add meaningful Chinese desc entries for the parameters
externalServiceType and configuration so each documents what the field
represents, expected values/format, and any required/optional constraints;
locate the strings within the APIAddExternalServiceConfigurationMsgDoc_zh_cn
class (the desc for the API and the desc properties for externalServiceType and
configuration) and replace the placeholders with clear, production-ready text.

In
`@header/src/main/java/org/zstack/header/core/external/service/APIQueryExternalServiceConfigurationMsg.java`:
- Around line 22-23: The example query string in
APIQueryExternalServiceConfigurationMsg.__example__ is missing the '=' and
should match the other APIQuery*Msg patterns; update the return to use "uuid=" +
uuid() so the example becomes Collections.singletonList("uuid=" + uuid());
modify the __example__ method in class APIQueryExternalServiceConfigurationMsg
accordingly.
♻️ Duplicate comments (5)
header/src/main/java/org/zstack/header/core/external/service/APIQueryExternalServiceConfigurationMsgDoc_zh_cn.groovy (1)

11-22: API 文档描述仍为占位符

Line 11 的 desc 仍是占位文本 "在这里填写API描述",Line 22 的 desc 为空。请补全有意义的 API 描述文本。

header/src/main/java/org/zstack/header/core/external/service/ExternalServiceConfigurationVO_.java (1)

17-18: 元模型属性类型参数错误(未修复)

createDatelastOpDateSingularAttribute 泛型参数错误地使用了 PluginDriverVO,应该使用 ExternalServiceConfigurationVO。这会导致 JPA Criteria 查询类型不匹配问题。

🐛 修复建议
-    public static volatile SingularAttribute<PluginDriverVO, Timestamp> createDate;
-    public static volatile SingularAttribute<PluginDriverVO, Timestamp> lastOpDate;
+    public static volatile SingularAttribute<ExternalServiceConfigurationVO, Timestamp> createDate;
+    public static volatile SingularAttribute<ExternalServiceConfigurationVO, Timestamp> lastOpDate;

同时,修复后可移除未使用的 PluginDriverVO 导入(Line 3)。

header/src/main/java/org/zstack/header/core/external/service/APIQueryExternalServiceConfigurationReplyDoc_zh_cn.groovy (1)

8-8: 文档占位符和描述字段未正确填写。

  • Line 8: title 仍为占位文本 "在这里输入结构的名称"
  • Line 13: inventoriesdesc 为字面值 "null"
  • Line 20: successdesc 为空
建议修复
-	title "在这里输入结构的名称"
+	title "查询外部服务配置返回"
	ref {
		name "inventories"
		path "org.zstack.header.core.external.service.APIQueryExternalServiceConfigurationReply.inventories"
-		desc "null"
+		desc "外部服务配置清单列表"
	field {
		name "success"
-		desc ""
+		desc "操作是否成功"

Also applies to: 13-13, 20-20

core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java (2)

241-244: 同步签名不一致,可能导致并发问题

APIAddExternalServiceConfigurationMsg 使用 msg.getExternalServiceType() 作为同步签名(Line 183),而 APIUpdateExternalServiceConfigurationMsg(Line 243)和 APIDeleteExternalServiceConfigurationMsg(Line 294)使用 msg.getUuid()

这导致:

  • 同一 serviceType 的添加操作互斥
  • 同一 uuid 的更新/删除操作互斥
  • 但添加与更新/删除操作之间无法互斥

建议统一同步粒度,可以在更新/删除时先查询 VO 获取 serviceType,或统一使用 uuid

Also applies to: 292-294


329-330: results 列表存在并发写入风险

While 循环中的 CloudBusCallBack 可能并发执行,而 ArrayList 非线程安全,存在数据竞争风险。

♻️ 建议修复
-        final List<ApplyExternalConfigurationResult> results = new ArrayList<>();
+        final List<ApplyExternalConfigurationResult> results = java.util.Collections.synchronizedList(new ArrayList<>());
🧹 Nitpick comments (4)
core/src/main/java/org/zstack/core/externalservice/ExternalService.java (1)

19-22: 接口方法缺少 Javadoc 注释

根据编码规范,接口方法必须配有有效的 Javadoc 注释。新增的 getServiceType()externalConfig(String serviceType) 方法缺少文档说明,建议补充:

📝 建议修改
+    /**
+     * Returns the type identifier for this external service.
+     * `@return` the service type string
+     */
     String getServiceType();

+    /**
+     * Applies external configuration for the specified service type.
+     * `@param` serviceType the type of service to configure
+     */
     void externalConfig(String serviceType);
externalservice/src/main/java/org/zstack/externalservice/cronjob/CronJobImpl.java (1)

37-40: 建议将服务类型定义为常量

"CronJob" 字符串直接硬编码,根据编码规范应避免魔法值。建议提取为常量以提升可维护性。

♻️ 可选重构
 public class CronJobImpl extends AbstractLocalExternalService implements CronJob {
+    public static final String SERVICE_TYPE = "CronJob";
+
     ExternalServiceCapabilities capabilities = ExternalServiceCapabilitiesBuilder
             .build()
             .reloadConfig(true);

     `@Override`
     public String getServiceType() {
-        return "CronJob";
+        return SERVICE_TYPE;
     }
header/src/main/java/org/zstack/header/core/external/service/ApplyExternalServiceConfigurationMsg.java (1)

16-18: 建议对 serviceType 进行 trim 处理

根据编码规范,应检查来自 Message 的参数是否做过 trim,防止用户输入带有空格或换行符。

♻️ 建议修改
     public void setServiceType(String serviceType) {
-        this.serviceType = serviceType;
+        this.serviceType = serviceType != null ? serviceType.trim() : null;
     }
header/src/main/java/org/zstack/header/core/external/service/APIAddExternalServiceConfigurationMsg.java (1)

22-23: 建议为 externalServiceType 添加 maxLength 约束

configurationdescription 字段都有 maxLength 约束,但 externalServiceType 没有。建议添加合理的长度限制以保持一致性和防止异常输入。

♻️ 建议修复
-    `@APIParam`
+    `@APIParam`(maxLength = 255)
     private String externalServiceType;

@MatheMatrix MatheMatrix force-pushed the sync/ya.wang/ZCF-19@@2 branch 2 times, most recently from 2eb6985 to 6e3835e Compare January 19, 2026 09:46
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In
`@externalservice/src/main/java/org/zstack/externalservice/cronjob/CronJobImpl.java`:
- Around line 113-114: The empty externalConfig(String serviceType)
implementation in CronJobImpl silently ignores configuration requests; update
externalConfig in class CronJobImpl to perform parameter validation on
serviceType and either (a) apply configuration if the service supports it, or
(b) explicitly reject unsupported requests by throwing a clear exception (e.g.,
UnsupportedOperationException or IllegalArgumentException) or logging an error
and returning a failure status; include a brief JavaDoc or comment on supported
serviceType values so callers know expected behavior.
- Around line 38-40: Replace the hardcoded service type string in
CronJobImpl.getServiceType() with the shared constant from CronJobFactory to
avoid a magic string; specifically, return the CronJobFactory.type (or its
string representation if needed) instead of "CronJob" so the implementation
reuses the public static final ExternalServiceType defined in CronJobFactory.

In
`@header/src/main/java/org/zstack/header/core/external/service/APIAddExternalServiceConfigurationMsg.java`:
- Around line 54-56: The audit method in APIAddExternalServiceConfigurationMsg
currently assumes ((APIAddExternalServiceConfigurationEvent) rsp).getInventory()
is non-null, risking an NPE; update APIAddExternalServiceConfigurationMsg.audit
to safely cast rsp to APIAddExternalServiceConfigurationEvent, check
rsp.isSuccess() && event != null && event.getInventory() != null before calling
getUuid(), and pass an empty string to new APIAuditor.Result when any of those
checks fail (also guard against rsp not being an instance of
APIAddExternalServiceConfigurationEvent).
♻️ Duplicate comments (7)
header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationMsgDoc_zh_cn.groovy (1)

10-20: 文档描述仍是占位符/空字符串

顶层与 request 说明为空,会导致文档缺失关键信息,建议补全实际描述。

📝 建议补充
-    desc """在这里填写API描述"""
+    desc """更新外部服务配置"""
...
-            desc """"""
+            desc """更新外部服务配置请求"""
header/src/main/java/org/zstack/header/core/external/service/APIAddExternalServiceConfigurationMsgDoc_zh_cn.groovy (1)

10-38: 文档占位符与空字段说明需补全

API 描述与关键字段说明为空,会影响文档可读性。

📝 建议补充
-    desc """在这里填写API描述"""
+    desc """添加外部服务配置"""
...
-					desc ""
+					desc "外部服务类型(如 Prometheus2/FluentBit)"
...
-					desc ""
+					desc "配置内容(建议说明格式/示例)"
header/src/main/java/org/zstack/header/core/external/service/APIQueryExternalServiceConfigurationMsg.java (1)

23-23: 查询示例缺少 = 可能导致条件格式异常

建议改为 uuid= + uuid(),避免生成无效条件字符串。Based on learnings, 查询消息 example 返回条件字符串列表的写法是正确的。

♻️ 建议修正
-    public static List<String> __example__() {return Collections.singletonList("uuid" + uuid());}
+    public static List<String> __example__() {return Collections.singletonList("uuid=" + uuid());}
header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationMsg.java (1)

25-26: description 字段应设为可选

当前 @APIParam 未设置 required = false,导致更新时 description 为必填。这与 APIAddExternalServiceConfigurationMsgdescription 为可选(required = false, maxLength = 2048)的设计不一致,且不支持部分更新场景。

🐛 修复建议
-    `@APIParam`
+    `@APIParam`(required = false, maxLength = 2048)
     private String description;
header/src/main/java/org/zstack/header/core/external/service/ExternalServiceConfigurationVO_.java (1)

3-3: 元模型属性类型参数错误

createDatelastOpDateSingularAttribute 泛型参数错误地使用了 PluginDriverVO,应为 ExternalServiceConfigurationVO。此问题会导致 JPA Criteria 查询类型不匹配。修复后 PluginDriverVO 导入将变为未使用。

🐛 修复建议
 package org.zstack.header.core.external.service;

-import org.zstack.header.core.external.plugin.PluginDriverVO;
 import org.zstack.header.vo.ResourceVO_;

 import javax.persistence.metamodel.SingularAttribute;
 import java.sql.Timestamp;

 /**
  * `@Author`: ya.wang
  * `@Date`: 1/15/26 1:30 AM
  */
 public class ExternalServiceConfigurationVO_ extends ResourceVO_ {
     public static volatile SingularAttribute<ExternalServiceConfigurationVO, String> serviceType;
     public static volatile SingularAttribute<ExternalServiceConfigurationVO, String> configuration;
     public static volatile SingularAttribute<ExternalServiceConfigurationVO, String> description;
-    public static volatile SingularAttribute<PluginDriverVO, Timestamp> createDate;
-    public static volatile SingularAttribute<PluginDriverVO, Timestamp> lastOpDate;
+    public static volatile SingularAttribute<ExternalServiceConfigurationVO, Timestamp> createDate;
+    public static volatile SingularAttribute<ExternalServiceConfigurationVO, Timestamp> lastOpDate;
 }

Also applies to: 17-18

core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java (2)

241-244: 同步签名不一致问题仍未解决。

APIAddExternalServiceConfigurationMsg 使用 msg.getExternalServiceType() 作为同步签名(Line 183),而 APIUpdateExternalServiceConfigurationMsgAPIDeleteExternalServiceConfigurationMsg 使用 msg.getUuid()(Lines 243, 294)。

这导致同步粒度不一致:

  • 同一 serviceType 的添加操作互斥
  • 同一 uuid 的更新/删除操作互斥
  • 但对同一配置的添加和更新/删除操作之间可能产生竞态条件

329-330: results 列表在并发回调中使用非线程安全集合。

CloudBus 回调可能并发执行,ArrayList 不是线程安全的,存在写入竞争与数据损坏风险。

♻️ 建议修改
-        final List<ApplyExternalConfigurationResult> results = new ArrayList<>();
+        final List<ApplyExternalConfigurationResult> results =
+                java.util.Collections.synchronizedList(new ArrayList<>());
🧹 Nitpick comments (4)
header/src/main/java/org/zstack/header/core/external/service/APIDeleteExternalServiceConfigurationMsg.java (1)

21-22: 建议为 uuid 添加 maxLength 约束

APIUpdateExternalServiceConfigurationMsg 中的 uuid 字段设置了 maxLength = 32,建议此处保持一致以确保输入验证的统一性。

♻️ 建议修改
-    `@APIParam`(resourceType = ExternalServiceConfigurationVO.class, successIfResourceNotExisting = true)
+    `@APIParam`(resourceType = ExternalServiceConfigurationVO.class, maxLength = 32, successIfResourceNotExisting = true)
     private String uuid;
header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationEvent.java (1)

22-29: __example__() 方法与 APIAddExternalServiceConfigurationEvent 不一致。

相比 APIAddExternalServiceConfigurationEvent.__example__() 设置了 uuidserviceTypeconfiguration,此处的 __example__() 仅设置了 uuid,导致生成的 API 文档示例不完整。

♻️ 建议补充示例字段
     public static APIUpdateExternalServiceConfigurationEvent __example__() {
         APIUpdateExternalServiceConfigurationEvent event = new APIUpdateExternalServiceConfigurationEvent();
         ExternalServiceConfigurationInventory inv = new ExternalServiceConfigurationInventory();
 
         inv.setUuid(uuid());
+        inv.setServiceType("Prometheus2");
+        inv.setConfiguration("{}");
         event.setInventory(inv);
         return event;
     }
core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java (1)

159-159: 方法签名格式问题。

handle 方法参数后有多余空格,与其他 handle 方法风格不一致。

♻️ 建议修改
-    private void handle(APIAddExternalServiceConfigurationMsg msg ){
+    private void handle(APIAddExternalServiceConfigurationMsg msg) {
header/src/main/java/org/zstack/header/core/external/service/APIAddExternalServiceConfigurationMsg.java (1)

22-27: 建议对 externalServiceTypeconfiguration 参数进行 trim 处理。

根据编码规范,用户可能复制粘贴带有空格或换行符的数据。建议在 Manager 处理时对这些参数进行 trim。

As per coding guidelines, "注意检查来自 Message 的参数是否做过 trim"。

Comment on lines +113 to +114
@Override
public void externalConfig(String serviceType){}
Copy link

@coderabbitai coderabbitai bot Jan 19, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

externalConfig 为空实现可能导致配置下发被静默忽略。

如果该服务不支持外部配置,建议至少做参数校验并显式失败或注释说明,避免误配置被吞掉。As per coding guidelines, keep behavior explicit and self-explanatory.

🐛 建议修改(示例:参数校验 + 明确无配置)
     `@Override`
-    public void externalConfig(String serviceType){}
+    public void externalConfig(String serviceType) {
+        if (!StringUtils.equals(serviceType, getServiceType())) {
+            throw new OperationFailureException(operr(String.format(
+                    "unexpected serviceType: %s", serviceType)));
+        }
+        // No external config for CronJob currently.
+    }
🤖 Prompt for AI Agents
In
`@externalservice/src/main/java/org/zstack/externalservice/cronjob/CronJobImpl.java`
around lines 113 - 114, The empty externalConfig(String serviceType)
implementation in CronJobImpl silently ignores configuration requests; update
externalConfig in class CronJobImpl to perform parameter validation on
serviceType and either (a) apply configuration if the service supports it, or
(b) explicitly reject unsupported requests by throwing a clear exception (e.g.,
UnsupportedOperationException or IllegalArgumentException) or logging an error
and returning a failure status; include a brief JavaDoc or comment on supported
serviceType values so callers know expected behavior.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Comment from ya.wang:

对于目前还不支持的类型, 默认返回空即可

Copy link

Choose a reason for hiding this comment

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

@zstack-robot-2 感谢澄清!我理解了,对于当前不支持外部配置的服务类型(如 CronJob),空实现是有意为之的设计,可以安全地忽略配置请求。这种做法在当前场景下是合理的。

/_/
( o.o )

^ < noted!


✏️ Learnings added
Learnt from: zstack-robot-2
Repo: MatheMatrix/zstack PR: 3216
File: externalservice/src/main/java/org/zstack/externalservice/cronjob/CronJobImpl.java:113-114
Timestamp: 2026-01-19T11:30:43.331Z
Learning: In the ZStack external service framework, for service types that do not support external configuration (e.g., CronJobImpl), the externalConfig(String serviceType) method can have an empty implementation. This allows unsupported service types to safely ignore configuration requests without throwing exceptions.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: zstack-robot-1
Repo: MatheMatrix/zstack PR: 3216
File: core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java:194-217
Timestamp: 2026-01-19T05:24:15.237Z
Learning: In ExternalServiceManagerImpl, when external service configuration creation or deletion fails during multi-node propagation (applyExternalServiceConfigurationToAllNodes), the design intentionally does not implement automatic rollback. Failed configurations should be cleaned up manually via the API delete endpoint.

Learnt from: zstack-robot-1
Repo: MatheMatrix/zstack PR: 2360
File: network/src/main/java/org/zstack/network/l3/L3BasicNetwork.java:449-490
Timestamp: 2025-08-04T04:48:19.103Z
Learning: ZStack项目在cherry-pick操作中,即使发现了性能优化机会(如IP地址批量保存的内存优化),也严格遵循不做额外修改的政策,优先保证cherry-pick的完整性和一致性。

Comment on lines 54 to 57
public Result audit(APIMessage msg, APIEvent rsp) {
return new APIAuditor.Result(rsp.isSuccess() ? ((APIAddExternalServiceConfigurationEvent) rsp).getInventory().getUuid() : "", ExternalServiceConfigurationVO.class);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

audit() 方法存在潜在 NPE 风险。

rsp.isSuccess()truegetInventory() 返回 null 时,调用 getInventory().getUuid() 会抛出 NullPointerException

🐛 建议修复
     `@Override`
     public Result audit(APIMessage msg, APIEvent rsp) {
-        return new APIAuditor.Result(rsp.isSuccess() ? ((APIAddExternalServiceConfigurationEvent) rsp).getInventory().getUuid() : "", ExternalServiceConfigurationVO.class);
+        APIAddExternalServiceConfigurationEvent event = (APIAddExternalServiceConfigurationEvent) rsp;
+        String uuid = rsp.isSuccess() && event.getInventory() != null ? event.getInventory().getUuid() : "";
+        return new APIAuditor.Result(uuid, ExternalServiceConfigurationVO.class);
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public Result audit(APIMessage msg, APIEvent rsp) {
return new APIAuditor.Result(rsp.isSuccess() ? ((APIAddExternalServiceConfigurationEvent) rsp).getInventory().getUuid() : "", ExternalServiceConfigurationVO.class);
}
public Result audit(APIMessage msg, APIEvent rsp) {
APIAddExternalServiceConfigurationEvent event = (APIAddExternalServiceConfigurationEvent) rsp;
String uuid = rsp.isSuccess() && event.getInventory() != null ? event.getInventory().getUuid() : "";
return new APIAuditor.Result(uuid, ExternalServiceConfigurationVO.class);
}
🤖 Prompt for AI Agents
In
`@header/src/main/java/org/zstack/header/core/external/service/APIAddExternalServiceConfigurationMsg.java`
around lines 54 - 56, The audit method in APIAddExternalServiceConfigurationMsg
currently assumes ((APIAddExternalServiceConfigurationEvent) rsp).getInventory()
is non-null, risking an NPE; update APIAddExternalServiceConfigurationMsg.audit
to safely cast rsp to APIAddExternalServiceConfigurationEvent, check
rsp.isSuccess() && event != null && event.getInventory() != null before calling
getUuid(), and pass an empty string to new APIAuditor.Result when any of those
checks fail (also guard against rsp not being an instance of
APIAddExternalServiceConfigurationEvent).

@MatheMatrix MatheMatrix force-pushed the sync/ya.wang/ZCF-19@@2 branch 2 times, most recently from 1ca0216 to 91ae64c Compare January 19, 2026 10:15
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In
`@core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java`:
- Around line 417-422: In ExternalServiceManagerImpl iterate over services, wrap
the call to ExternalService.externalConfig(serviceType) in a try-catch so any
thrown exception is caught and converted into an ErrorCode (or an appropriate
error via the system's ErrorCode builder) and passed to completion.fail(...)
instead of letting the exception propagate; on success continue to call
completion.success(serviceType) and return. Ensure the catch logs or includes
the exception message and references ExternalService.externalConfig,
completion.success and completion.fail/ErrorCode conversion so reviewers can
find the change.

In
`@header/src/main/java/org/zstack/header/core/external/service/APIDeleteExternalServiceConfigurationMsg.java`:
- Around line 15-19: 当前 REST 路径使用单数 "/external/service/configuration/{uuid}"
不符合项目约定;请在 APIDeleteExternalServiceConfigurationMsg 的 `@RestRequest` 注解中将 path 改为
"/external/service/configurations/{uuid}",并在
QueryExternalServiceConfigurationAction(及任何其它引用该路径 的类或路由映射)中把
"/external/service/configuration" 改为
"/external/service/configurations",同时更新相关单元测试、文档和调用方以匹配新的复数路径。

In
`@header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationEventDoc_zh_cn.groovy`:
- Around line 10-23: Update the missing/placeholder descriptions in the
APIUpdateExternalServiceConfigurationEventDoc_zh_cn.groovy doc: replace the ref
block's desc for name "inventory" (path
"org.zstack.header.core.external.service.APIUpdateExternalServiceConfigurationEvent.inventory",
clz ExternalServiceConfigurationInventory.class) with a meaningful Chinese
description such as "外部服务配置清单", and set the field block's desc for name
"success" to a meaningful description such as "操作是否成功"; ensure both desc values
are non-empty and accurately reflect the represented data.
♻️ Duplicate comments (9)
externalservice/src/main/java/org/zstack/externalservice/cronjob/CronJobImpl.java (2)

37-40: 复用已有的服务类型常量,避免魔法字符串。
建议返回 CronJobFactory 中已定义的类型常量而非硬编码字符串,以保证一致性。


113-114: externalConfig 为空实现会静默忽略配置请求。
建议至少做 serviceType 校验并显式失败或说明不支持,以免误配置被吞掉。

header/src/main/java/org/zstack/header/core/external/service/APIDeleteExternalServiceConfigurationMsgDoc_zh_cn.groovy (1)

12-21: 请求级别描述为空。

request 块中的 desc 仍为空(第 20 行),建议填写简短的请求说明,例如描述此 API 的用途或调用场景。

建议修复
             clz APIDeleteExternalServiceConfigurationMsg.class

-            desc """"""
+            desc """根据UUID删除指定的外部服务配置"""
header/src/main/java/org/zstack/header/core/external/service/APIQueryExternalServiceConfigurationMsg.java (1)

22-23: 查询条件字符串缺少 = 符号

根据其他 APIQuery*Msg 类的实现惯例(如 APIQueryDiskOfferingMsg 使用 "uuid=" + uuid()),此处应添加等号以保持一致性。

♻️ 建议修复
-    public static List<String> __example__() {return Collections.singletonList("uuid" + uuid());}
+    public static List<String> __example__() {return Collections.singletonList("uuid=" + uuid());}
header/src/main/java/org/zstack/header/core/external/service/APIQueryExternalServiceConfigurationReplyDoc_zh_cn.groovy (1)

6-23: 补全响应文档标题与字段描述

title 仍是占位文本,inventoriesdesc 为 "null",successdesc 为空,建议替换为清晰说明以生成完整的 API 文档。

📝 建议修复
 doc {

-	title "在这里输入结构的名称"
+	title "查询外部服务配置响应"

 	ref {
 		name "inventories"
 		path "org.zstack.header.core.external.service.APIQueryExternalServiceConfigurationReply.inventories"
-		desc "null"
+		desc "外部服务配置列表"
 		type "List"
 		since "5.4.6"
 		clz ExternalServiceConfigurationInventory.class
 	}
 	field {
 		name "success"
-		desc ""
+		desc "操作是否成功"
 		type "boolean"
 		since "5.4.6"
 	}
header/src/main/java/org/zstack/header/core/external/service/ExternalServiceConfigurationVO_.java (1)

3-3: 元模型属性类型参数错误

createDatelastOpDateSingularAttribute 泛型参数错误地使用了 PluginDriverVO,应该使用 ExternalServiceConfigurationVO。这会导致 JPA Criteria 查询在使用这些元模型字段时出现类型不匹配问题。同时,修复后 PluginDriverVO 的导入将变为未使用,应一并移除。

🐛 修复建议
 package org.zstack.header.core.external.service;

-import org.zstack.header.core.external.plugin.PluginDriverVO;
 import org.zstack.header.vo.ResourceVO_;

 import javax.persistence.metamodel.SingularAttribute;
 import java.sql.Timestamp;

 /**
  * `@Author`: ya.wang
  * `@Date`: 1/15/26 1:30 AM
  */
 public class ExternalServiceConfigurationVO_ extends ResourceVO_ {
     public static volatile SingularAttribute<ExternalServiceConfigurationVO, String> serviceType;
     public static volatile SingularAttribute<ExternalServiceConfigurationVO, String> configuration;
     public static volatile SingularAttribute<ExternalServiceConfigurationVO, String> description;
-    public static volatile SingularAttribute<PluginDriverVO, Timestamp> createDate;
-    public static volatile SingularAttribute<PluginDriverVO, Timestamp> lastOpDate;
+    public static volatile SingularAttribute<ExternalServiceConfigurationVO, Timestamp> createDate;
+    public static volatile SingularAttribute<ExternalServiceConfigurationVO, Timestamp> lastOpDate;
 }

Also applies to: 17-18

header/src/main/java/org/zstack/header/core/external/service/APIAddExternalServiceConfigurationMsg.java (1)

54-56: audit() 方法存在潜在 NPE 风险。

rsp.isSuccess()truegetInventory() 返回 null 时,调用 getInventory().getUuid() 会抛出 NullPointerException

🐛 建议修复
     `@Override`
     public Result audit(APIMessage msg, APIEvent rsp) {
-        return new APIAuditor.Result(rsp.isSuccess() ? ((APIAddExternalServiceConfigurationEvent) rsp).getInventory().getUuid() : "", ExternalServiceConfigurationVO.class);
+        APIAddExternalServiceConfigurationEvent event = (APIAddExternalServiceConfigurationEvent) rsp;
+        String uuid = rsp.isSuccess() && event.getInventory() != null ? event.getInventory().getUuid() : "";
+        return new APIAuditor.Result(uuid, ExternalServiceConfigurationVO.class);
     }
core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java (2)

183-186: 同步签名不一致,可能导致并发问题。

APIAddExternalServiceConfigurationMsg 使用 msg.getExternalServiceType() 作为同步签名,而 APIUpdateExternalServiceConfigurationMsgAPIDeleteExternalServiceConfigurationMsg(Line 245, 296)使用 msg.getUuid()。这导致:

  • 同一 serviceType 的添加操作互斥
  • 同一 uuid 的更新/删除操作互斥
  • 但添加和更新/删除操作之间可能并发执行,造成竞态条件

建议统一使用 serviceType 作为同步粒度。


331-332: results 列表可能被并发回调写入,建议使用线程安全集合。

CloudBus 回调可能并发执行,ArrayList 不是线程安全的,存在写入竞争与数据损坏风险。

♻️ 建议修改
-        final List<ApplyExternalConfigurationResult> results = new ArrayList<>();
+        final List<ApplyExternalConfigurationResult> results =
+                java.util.Collections.synchronizedList(new ArrayList<>());
🧹 Nitpick comments (3)
core/src/main/java/org/zstack/core/externalservice/ExternalService.java (1)

19-22: 建议为新增接口方法添加 Javadoc 注释。

根据编码规范,接口方法必须配有有效的 Javadoc 注释。另外,externalConfig(String serviceType)serviceType 参数与 getServiceType() 返回值似乎有重叠,如果服务实现类已经知道自己的类型,为何还需要传入 serviceType?建议确认这是否为有意设计(例如用于跨服务类型调用场景)。

建议添加 Javadoc
     void reload();
 
+    /**
+     * Returns the service type identifier for this external service.
+     * `@return` service type string
+     */
     String getServiceType();
 
+    /**
+     * Applies external configuration for the specified service type.
+     * `@param` serviceType the service type to apply configuration for
+     */
     void externalConfig(String serviceType);
header/src/main/java/org/zstack/header/core/external/service/ExternalServiceConfigurationVO.java (1)

16-28: 建议为数据库列添加长度约束和索引。

根据 APIAddExternalServiceConfigurationMsg 中的定义,configuration 字段最大长度为 65535,description 最大长度为 2048。建议:

  1. configuration 字段指定 columnDefinition = "TEXT" 以支持大文本存储
  2. serviceType 添加索引,因为它是查询和同步操作的关键字段
  3. 考虑为字符串字段添加 length 约束
♻️ 建议修改
 `@Entity`
-@Table
+@Table(name = "ExternalServiceConfigurationVO", indexes = {
+    `@Index`(name = "idxExternalServiceConfigurationVOserviceType", columnList = "serviceType")
+})
 public class ExternalServiceConfigurationVO extends ResourceVO implements ToInventory {
-    `@Column`
+    `@Column`(length = 128)
     private String serviceType;
-    `@Column`
+    `@Column`(columnDefinition = "TEXT")
     private String configuration;
-    `@Column`
+    `@Column`(length = 2048)
     private String description;
header/src/main/java/org/zstack/header/core/external/service/APIAddExternalServiceConfigurationMsg.java (1)

15-20: API 路径应使用复数形式。

根据编码规范,REST API 路径针对资源应使用复数形式。当前路径 /external/service/configuration 应改为 /external/services/configurations

♻️ 建议修改
 `@RestRequest`(
-        path = "/external/service/configuration",
+        path = "/external/services/configurations",
         method = HttpMethod.POST,
         parameterName = "params",
         responseClass = APIAddExternalServiceConfigurationEvent.class
 )

Comment on lines +15 to +19
@RestRequest(
path = "/external/service/configuration/{uuid}",
responseClass = APIDeleteExternalServiceConfigurationEvent.class,
method = HttpMethod.DELETE
)
Copy link

@coderabbitai coderabbitai bot Jan 19, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Search for external service related API paths
rg -n "path\s*=\s*\"/external" --type java -A1 -B1 | head -50

Repository: MatheMatrix/zstack

Length of output: 4229


🏁 Script executed:

# Search for configuration-related API paths to see if singular or plural is used
rg -n "path\s*=\s*\"[^\"]*configuration" --type java -A1 -B1 | head -50

Repository: MatheMatrix/zstack

Length of output: 4454


🏁 Script executed:

# Search for other delete API patterns to understand the project convention
rg -n "APIDelete.*Msg\.java" --type java | head -20

Repository: MatheMatrix/zstack

Length of output: 44


🏁 Script executed:

# Check the specific file to understand its structure
cat -n header/src/main/java/org/zstack/header/core/external/service/APIDeleteExternalServiceConfigurationMsg.java

Repository: MatheMatrix/zstack

Length of output: 1764


API 路径应使用复数形式。

根据编码规范和项目现有模式,API 资源路径应使用复数形式。将 /external/service/configuration/{uuid} 改为 /external/service/configurations/{uuid} 可与其他配置相关 API 保持一致(如 /log/configurations/template-configurations//resource-configurations/ 等)。

同时需检查 QueryExternalServiceConfigurationAction 中对应的路径 /external/service/configuration 是否也应改为 /external/service/configurations

🤖 Prompt for AI Agents
In
`@header/src/main/java/org/zstack/header/core/external/service/APIDeleteExternalServiceConfigurationMsg.java`
around lines 15 - 19, 当前 REST 路径使用单数 "/external/service/configuration/{uuid}"
不符合项目约定;请在 APIDeleteExternalServiceConfigurationMsg 的 `@RestRequest` 注解中将 path 改为
"/external/service/configurations/{uuid}",并在
QueryExternalServiceConfigurationAction(及任何其它引用该路径 的类或路由映射)中把
"/external/service/configuration" 改为
"/external/service/configurations",同时更新相关单元测试、文档和调用方以匹配新的复数路径。

Copy link
Owner

Choose a reason for hiding this comment

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

Comment from ya.wang:

reject

Copy link

Choose a reason for hiding this comment

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

@MatheMatrix Thank you for the feedback. I've noted your decision to keep the current path structure. I'll move on.


🧠 Learnings used
Learnt from: zstack-robot-2
Repo: MatheMatrix/zstack PR: 2435
File: header/src/main/java/org/zstack/header/storage/snapshot/group/APICheckMemorySnapshotGroupConflictMsg.java:8-12
Timestamp: 2025-08-14T06:49:52.559Z
Learning: In the ZStack project, memory snapshot group conflict detection API should use plural resource paths with UUID path variables following the pattern "/memory-snapshots/groups/{uuid}/conflict-detection" rather than "/memory-snapshots/group/conflict-detection" to comply with API design standards.

Learnt from: zstack-robot-2
Repo: MatheMatrix/zstack PR: 2419
File: plugin/sdnController/src/main/java/org/zstack/sdnController/header/APIPullSdnControllerTenantMsg.java:13-18
Timestamp: 2025-08-13T02:37:50.601Z
Learning: In the ZStack codebase, API paths for action endpoints use singular forms for sub-resources, such as "/sdn-controllers/{uuid}/tenant/actions", which differs from general REST conventions that prefer plurals. This is the established pattern that should be followed for consistency.

Comment on lines +10 to +23
ref {
name "inventory"
path "org.zstack.header.core.external.service.APIUpdateExternalServiceConfigurationEvent.inventory"
desc "null"
type "ExternalServiceConfigurationInventory"
since "5.4.6"
clz ExternalServiceConfigurationInventory.class
}
field {
name "success"
desc ""
type "boolean"
since "5.4.6"
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

字段描述不完整

以下字段的 desc 描述为空或为占位符:

  • Line 13: inventorydesc"null",应填写实际描述(如 "外部服务配置清单")。
  • Line 20: successdesc 为空字符串,应填写实际描述(如 "操作是否成功")。
📝 建议修复
 	ref {
 		name "inventory"
 		path "org.zstack.header.core.external.service.APIUpdateExternalServiceConfigurationEvent.inventory"
-		desc "null"
+		desc "外部服务配置清单"
 		type "ExternalServiceConfigurationInventory"
 		since "5.4.6"
 		clz ExternalServiceConfigurationInventory.class
 	}
 	field {
 		name "success"
-		desc ""
+		desc "操作是否成功"
 		type "boolean"
 		since "5.4.6"
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ref {
name "inventory"
path "org.zstack.header.core.external.service.APIUpdateExternalServiceConfigurationEvent.inventory"
desc "null"
type "ExternalServiceConfigurationInventory"
since "5.4.6"
clz ExternalServiceConfigurationInventory.class
}
field {
name "success"
desc ""
type "boolean"
since "5.4.6"
}
ref {
name "inventory"
path "org.zstack.header.core.external.service.APIUpdateExternalServiceConfigurationEvent.inventory"
desc "External service configuration inventory"
type "ExternalServiceConfigurationInventory"
since "5.4.6"
clz ExternalServiceConfigurationInventory.class
}
field {
name "success"
desc "Whether the operation was successful"
type "boolean"
since "5.4.6"
}
🤖 Prompt for AI Agents
In
`@header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationEventDoc_zh_cn.groovy`
around lines 10 - 23, Update the missing/placeholder descriptions in the
APIUpdateExternalServiceConfigurationEventDoc_zh_cn.groovy doc: replace the ref
block's desc for name "inventory" (path
"org.zstack.header.core.external.service.APIUpdateExternalServiceConfigurationEvent.inventory",
clz ExternalServiceConfigurationInventory.class) with a meaningful Chinese
description such as "外部服务配置清单", and set the field block's desc for name
"success" to a meaningful description such as "操作是否成功"; ensure both desc values
are non-empty and accurately reflect the represented data.

@MatheMatrix MatheMatrix force-pushed the sync/ya.wang/ZCF-19@@2 branch from 91ae64c to 74f4e14 Compare January 19, 2026 11:36
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java`:
- Around line 332-334: In ExternalServiceManagerImpl within the method
applyExternalServiceConfigurationToAllNodes, remove the stray extra semicolon
after the declaration of the synchronized list (the line declaring final
List<ApplyExternalConfigurationResult> results =
Collections.synchronizedList(new ArrayList<>());; ) so it becomes a single
semicolon-terminated statement; this cleans up the declaration of results and
avoids the redundant semicolon.
♻️ Duplicate comments (6)
testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy (3)

14303-14327: 同上:确认由生成器更新。
Based on learnings, 同上。


30484-30510: 同上:确认由生成器更新。
Based on learnings, 同上。


43553-43577: 同上:确认由生成器更新。
Based on learnings, 同上。

header/src/main/java/org/zstack/header/core/external/service/APIAddExternalServiceConfigurationMsg.java (1)

53-57: audit() 方法存在潜在 NPE 风险。

rsp.isSuccess()trueevt.getInventory() 返回 null 时,会抛出 NullPointerException。建议添加空值检查。

🐛 建议修复
     `@Override`
     public Result audit(APIMessage msg, APIEvent rsp) {
         APIAddExternalServiceConfigurationEvent evt = (APIAddExternalServiceConfigurationEvent) rsp;
-        return new Result(rsp.isSuccess() ? evt.getInventory().getUuid(): "", ExternalServiceConfigurationVO.class);
+        String uuid = rsp.isSuccess() && evt.getInventory() != null ? evt.getInventory().getUuid() : "";
+        return new Result(uuid, ExternalServiceConfigurationVO.class);
     }
header/src/main/java/org/zstack/header/core/external/service/APIQueryExternalServiceConfigurationMsgDoc_zh_cn.groovy (1)

6-23: 文档描述仍未补全。

desc 为空且文档说明未完善的问题已在之前评审中指出,请参照原意见处理。

core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java (1)

244-247: 同步签名粒度仍不一致。

创建使用 serviceType,而更新/删除使用 uuid,互斥粒度不一致的问题已在之前评审中指出,请保持一致性。

Also applies to: 296-298

🧹 Nitpick comments (1)
header/src/main/java/org/zstack/header/core/external/service/APIAddExternalServiceConfigurationMsg.java (1)

61-61: 示例中的服务类型应使用真实类型名称。

__example__() 方法中使用 "Prometheus2" 作为示例服务类型,建议使用实际支持的服务类型名称或在文档中说明这是占位符,以避免 API 使用者混淆。

@MatheMatrix MatheMatrix force-pushed the sync/ya.wang/ZCF-19@@2 branch 2 times, most recently from 4db7538 to 6b9d1bc Compare January 20, 2026 01:48
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@header/src/main/java/org/zstack/header/core/external/service/APIDeleteExternalServiceConfigurationEventDoc_zh_cn.groovy`:
- Around line 15-22: The desc line in the ref block for the field named "error"
(path
"org.zstack.header.core.external.service.APIDeleteExternalServiceConfigurationEvent.error")
incorrectly contains ",false" after the description string; remove the trailing
",false" from the desc line and place the boolean as its own DSL property (e.g.,
add a separate line like optional false or required false according to the DSL
used) immediately after the desc line so the ref block for name "error" is
syntactically correct.
♻️ Duplicate comments (3)
header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationMsg.java (1)

25-26: description 字段应设为可选以支持部分更新

当前 @APIParam 未设置 required = false,意味着更新时 description 是必填字段。这与 APIAddExternalServiceConfigurationMsgdescription 为可选字段(required = false)的设计不一致,且不符合更新操作应支持部分更新的最佳实践。

🐛 修复建议
-    `@APIParam`
+    `@APIParam`(required = false, maxLength = 2048)
     private String description;
core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java (2)

333-333: 多余的分号。

存在一个多余的 ;,建议移除以保持代码整洁。

🧹 建议修复
-        final List<ApplyExternalConfigurationResult> results = Collections.synchronizedList(new ArrayList<>());;
+        final List<ApplyExternalConfigurationResult> results = Collections.synchronizedList(new ArrayList<>());

244-247: 同步签名不一致,可能导致并发问题。

APIAddExternalServiceConfigurationMsg 使用 msg.getExternalServiceType() 作为同步签名(Line 186),而 APIUpdateExternalServiceConfigurationMsgAPIDeleteExternalServiceConfigurationMsg 使用 msg.getUuid()(Lines 246, 297)。

这导致同一 serviceType 的添加操作与更新/删除操作无法互斥,存在潜在竞态条件。

🔧 建议修复方案

方案一:在 Update/Delete 消息中添加 serviceType 字段,统一使用 serviceType 作为同步粒度。

方案二:在 getSyncSignature 中通过 UUID 查询获取 serviceType

`@Override`
public String getSyncSignature() {
    ExternalServiceConfigurationVO vo = dbf.findByUuid(msg.getUuid(), ExternalServiceConfigurationVO.class);
    String serviceType = vo != null ? vo.getServiceType() : msg.getUuid();
    return String.format("create-update-delete-external-service-configuration-%s", serviceType);
}

注意:方案二会引入额外的数据库查询开销。

Also applies to: 295-298

🧹 Nitpick comments (4)
header/src/main/java/org/zstack/header/core/external/service/ApplyExternalServiceConfigurationReply.java (1)

10-11: 建议:value 字段命名过于泛化

value 字段的含义不够明确,不清楚它代表什么类型的值(配置结果?应用状态?错误信息?)。根据编码规范"命名应尽量用完整的单词组合表达意图",建议使用更具描述性的名称。

可能的改进
 public class ApplyExternalServiceConfigurationReply extends MessageReply {
     private String managementNodeUuid;
-    private String value;
+    private String configurationResult;  // 或 appliedConfiguration / resultMessage
header/src/main/java/org/zstack/header/core/external/service/ExternalServiceConfigurationVO.java (2)

19-28: configuration 字段可能需要指定列类型为 TEXT。

configuration 字段预期存储 JSON 配置数据,默认的 VARCHAR(255) 可能不足以容纳复杂配置。建议明确指定列定义以支持较大的配置内容。

此外,serviceType 作为常用查询条件,建议添加索引以提升查询性能。

♻️ 建议修改
 `@Column`
+@Index
 private String serviceType;
-@Column
+@Column(columnDefinition = "TEXT")
 private String configuration;
 `@Column`
+@Column(length = 1024)
 private String description;

30-39: 代码组织建议:将 @PreUpdate 方法移至字段声明之后或类末尾。

preUpdate() 方法当前位于 getServiceType()setServiceType() 之间,影响代码可读性。建议将生命周期回调方法与 getter/setter 分离。

header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationEvent.java (1)

22-29: __example__ 方法示例不完整,与 APIAddExternalServiceConfigurationEvent 不一致。

对比 APIAddExternalServiceConfigurationEvent.__example__(),该方法还设置了 serviceTypeconfiguration。为了生成更完整的 API 文档,建议补充这些字段的示例值。

♻️ 建议修改
 public static APIUpdateExternalServiceConfigurationEvent __example__() {
     APIUpdateExternalServiceConfigurationEvent event = new APIUpdateExternalServiceConfigurationEvent();
     ExternalServiceConfigurationInventory inv = new ExternalServiceConfigurationInventory();

     inv.setUuid(uuid());
+    inv.setServiceType("Prometheus2");
+    inv.setConfiguration("{}");
     event.setInventory(inv);
     return event;
 }

Comment on lines +15 to +22
ref {
name "error"
path "org.zstack.header.core.external.service.APIDeleteExternalServiceConfigurationEvent.error"
desc "错误码,若不为null,则表示操作失败, 操作成功时该字段为null",false
type "ErrorCode"
since "5.4.6"
clz ErrorCode.class
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

ref 块中 desc 行语法异常

Line 18 的 ,false 放置在 desc 字符串后面,看起来像是要设置某个布尔属性(可能是 optional 或类似字段),但当前语法不正确。应该将其移到单独一行或使用正确的 DSL 语法。

建议修复
 	ref {
 		name "error"
 		path "org.zstack.header.core.external.service.APIDeleteExternalServiceConfigurationEvent.error"
-		desc "错误码,若不为null,则表示操作失败, 操作成功时该字段为null",false
+		desc "错误码,若不为null,则表示操作失败, 操作成功时该字段为null"
+		optional false
 		type "ErrorCode"
 		since "5.4.6"
 		clz ErrorCode.class
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ref {
name "error"
path "org.zstack.header.core.external.service.APIDeleteExternalServiceConfigurationEvent.error"
desc "错误码,若不为null,则表示操作失败, 操作成功时该字段为null",false
type "ErrorCode"
since "5.4.6"
clz ErrorCode.class
}
ref {
name "error"
path "org.zstack.header.core.external.service.APIDeleteExternalServiceConfigurationEvent.error"
desc "错误码,若不为null,则表示操作失败, 操作成功时该字段为null"
optional false
type "ErrorCode"
since "5.4.6"
clz ErrorCode.class
}
🤖 Prompt for AI Agents
In
`@header/src/main/java/org/zstack/header/core/external/service/APIDeleteExternalServiceConfigurationEventDoc_zh_cn.groovy`
around lines 15 - 22, The desc line in the ref block for the field named "error"
(path
"org.zstack.header.core.external.service.APIDeleteExternalServiceConfigurationEvent.error")
incorrectly contains ",false" after the description string; remove the trailing
",false" from the desc line and place the boolean as its own DSL property (e.g.,
add a separate line like optional false or required false according to the DSL
used) immediately after the desc line so the ref block for name "error" is
syntactically correct.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@core/src/main/java/org/zstack/core/externalservice/ExternalService.java`:
- Around line 19-22: 为接口方法添加 Javadoc 注释:在 ExternalService 接口中为 getServiceType()
和 externalConfig(String serviceType) 添加规范的 Javadoc
注释,分别说明方法的用途、返回值(getServiceType 返回的服务类型含义)及参数含义(externalConfig 的 serviceType
参数代表哪个服务或配置上下文),并在必要时加入 `@return` 和 `@param` 标签以满足编码规范与文档要求。

In
`@header/src/main/java/org/zstack/header/core/external/service/ApplyExternalServiceConfigurationMsg.java`:
- Around line 10-17: Update the setter in ApplyExternalServiceConfigurationMsg
to trim incoming values and validate emptiness: in setServiceType(String
serviceType) call serviceType = serviceType == null ? null : serviceType.trim()
(and if required by policy throw IllegalArgumentException or set to null when
the trimmed value is empty) before assigning to the serviceType field; ensure
getServiceType() returns the trimmed/stored value so downstream matching uses
the normalized string.
♻️ Duplicate comments (5)
header/src/main/java/org/zstack/header/core/external/service/APIDeleteExternalServiceConfigurationMsgDoc_zh_cn.groovy (1)

13-21: 补全 request 级别的描述,避免文档空白。
rest.request.desc 为空会导致生成的 API 文档缺少请求说明。

📝 建议补全文档描述
-            desc """"""
+            desc """删除指定外部服务配置的请求,支持可选删除模式与标签参数。"""
header/src/main/java/org/zstack/header/core/external/service/APIDeleteExternalServiceConfigurationEventDoc_zh_cn.groovy (1)

15-22: ref 块中 desc 行语法仍存在问题

Line 18 的 ,false 附加在 desc 字符串后面,这是不正确的 DSL 语法。应该将布尔值移到单独一行作为 optional 属性。

建议修复
 	ref {
 		name "error"
 		path "org.zstack.header.core.external.service.APIDeleteExternalServiceConfigurationEvent.error"
-		desc "错误码,若不为null,则表示操作失败, 操作成功时该字段为null",false
+		desc "错误码,若不为null,则表示操作失败, 操作成功时该字段为null"
+		optional false
 		type "ErrorCode"
 		since "5.4.6"
 		clz ErrorCode.class
 	}
header/src/main/java/org/zstack/header/core/external/service/APIQueryExternalServiceConfigurationMsgDoc_zh_cn.groovy (1)

11-22: 文档描述问题已在之前的审查中指出

此问题已在之前的审查评论中提出:desc 字段包含中文占位文本,且 request desc 为空。请参考之前的审查建议进行修复。

header/src/main/java/org/zstack/header/core/external/service/APIQueryExternalServiceConfigurationReplyDoc_zh_cn.groovy (1)

6-23: 补全文档标题与字段描述。
title 仍为占位文本,inventoriesdesc 为 "null",success 描述为空,建议补充清晰说明以便文档可读。

core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java (1)

185-187: 同步签名不一致可能导致并发互斥粒度错配。
create 使用 serviceTypeupdate/delete 使用 uuid,可能出现同一类型的创建与更新/删除并发穿透。建议统一同步粒度。

Also applies to: 245-247, 296-298

Comment on lines +19 to +22

String getServiceType();

void externalConfig(String serviceType);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Search for calls to externalConfig method and how the interface is used
rg -n -A3 -B3 'externalConfig\s*\(' --type=java | head -50

Repository: MatheMatrix/zstack

Length of output: 2071


🏁 Script executed:

# Read the ExternalService interface to see the complete definition
find . -type f -name "ExternalService.java" -path "*/core/src/*" | head -1 | xargs cat -n

Repository: MatheMatrix/zstack

Length of output: 645


🏁 Script executed:

# Check for Javadoc comments in the interface
rg -B5 'String getServiceType\(\)|void externalConfig' core/src/main/java/org/zstack/core/externalservice/ExternalService.java

Repository: MatheMatrix/zstack

Length of output: 224


为接口方法添加 Javadoc 注释

根据编码规范,接口方法必须配有有效的 Javadoc 注释。请为 getServiceType()externalConfig(String serviceType) 添加文档说明其用途。

🤖 Prompt for AI Agents
In `@core/src/main/java/org/zstack/core/externalservice/ExternalService.java`
around lines 19 - 22, 为接口方法添加 Javadoc 注释:在 ExternalService 接口中为 getServiceType()
和 externalConfig(String serviceType) 添加规范的 Javadoc
注释,分别说明方法的用途、返回值(getServiceType 返回的服务类型含义)及参数含义(externalConfig 的 serviceType
参数代表哪个服务或配置上下文),并在必要时加入 `@return` 和 `@param` 标签以满足编码规范与文档要求。

Comment on lines +10 to +17
private String serviceType;

public String getServiceType() {
return serviceType;
}

public void setServiceType(String serviceType) {
this.serviceType = serviceType;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

确保 serviceType 去空格并校验空值

Line 16 的 setter 直接赋值,若上游传入带空格/空值会导致服务类型匹配失败。建议在此处进行 trim(必要时也可校验空值)。As per coding guidelines, please trim message parameters.

♻️ 建议修改
 public void setServiceType(String serviceType) {
-    this.serviceType = serviceType;
+    this.serviceType = serviceType == null ? null : serviceType.trim();
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private String serviceType;
public String getServiceType() {
return serviceType;
}
public void setServiceType(String serviceType) {
this.serviceType = serviceType;
private String serviceType;
public String getServiceType() {
return serviceType;
}
public void setServiceType(String serviceType) {
this.serviceType = serviceType == null ? null : serviceType.trim();
}
🤖 Prompt for AI Agents
In
`@header/src/main/java/org/zstack/header/core/external/service/ApplyExternalServiceConfigurationMsg.java`
around lines 10 - 17, Update the setter in ApplyExternalServiceConfigurationMsg
to trim incoming values and validate emptiness: in setServiceType(String
serviceType) call serviceType = serviceType == null ? null : serviceType.trim()
(and if required by policy throw IllegalArgumentException or set to null when
the trimmed value is empty) before assigning to the serviceType field; ensure
getServiceType() returns the trimmed/stored value so downstream matching uses
the normalized string.

APIImpact
DBImpact

Resolves: ZCF-19

Change-Id: I726b676a6576716f6962646665707566676d6b65
@MatheMatrix MatheMatrix force-pushed the sync/ya.wang/ZCF-19@@2 branch from 6b9d1bc to de418a2 Compare January 20, 2026 02:06
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationMsg.java`:
- Around line 14-18: The REST path in the RestRequest annotation on
APIUpdateExternalServiceConfigurationMsg uses the singular "configuration";
change the path value to use the plural resource form (e.g., "configurations")
in the `@RestRequest` on APIUpdateExternalServiceConfigurationMsg so it follows
the API naming convention, and update any callers or documentation that
reference the old path string to the new
"/external/service/configurations/{uuid}" if present.
♻️ Duplicate comments (4)
header/src/main/java/org/zstack/header/core/external/service/APIQueryExternalServiceConfigurationReplyDoc_zh_cn.groovy (1)

8-22: 补全响应文档标题与字段描述

title 仍是占位文本,inventoriesdesc 为 “null”,successdesc 为空,建议替换为清晰、准确的说明,避免文档生成后出现无意义内容。

header/src/main/java/org/zstack/header/core/external/service/ExternalServiceConfigurationVO_.java (1)

17-18: 元模型属性类型参数错误

createDatelastOpDateSingularAttribute 泛型参数错误地使用了 PluginDriverVO,应该使用 ExternalServiceConfigurationVO。这会导致 JPA Criteria 查询在使用这些元模型字段时出现类型不匹配问题。

🐛 修复建议
-    public static volatile SingularAttribute<PluginDriverVO, Timestamp> createDate;
-    public static volatile SingularAttribute<PluginDriverVO, Timestamp> lastOpDate;
+    public static volatile SingularAttribute<ExternalServiceConfigurationVO, Timestamp> createDate;
+    public static volatile SingularAttribute<ExternalServiceConfigurationVO, Timestamp> lastOpDate;

同时,第 3 行的 PluginDriverVO 导入可以移除:

-import org.zstack.header.core.external.plugin.PluginDriverVO;
header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationMsg.java (1)

25-26: description 应为可选字段。 更新接口应允许部分更新,建议将 @APIParam 设为 required = false(并与新增接口的约束保持一致)。
(此问题已在历史评论中提出)

🔧 建议修改
-    `@APIParam`
+    `@APIParam`(required = false, maxLength = 2048)
     private String description;
core/src/main/java/org/zstack/core/externalservice/ExternalServiceManagerImpl.java (1)

244-247: 同步签名仍与创建流程不一致。 创建使用 serviceType,更新/删除使用 uuid,可能导致并发互斥粒度不一致。该问题已在历史评论中提出,建议保持一致的同步签名策略。

Also applies to: 296-298

🧹 Nitpick comments (1)
header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationEvent.java (1)

22-28: 示例对象建议补充字段以完善文档输出。 当前 __example__() 只设置 uuid,可同步填充 serviceType/configuration/description 以与其他事件示例保持一致性。

♻️ 可选修改
     public static APIUpdateExternalServiceConfigurationEvent __example__() {
         APIUpdateExternalServiceConfigurationEvent event = new APIUpdateExternalServiceConfigurationEvent();
         ExternalServiceConfigurationInventory inv = new ExternalServiceConfigurationInventory();
 
         inv.setUuid(uuid());
+        inv.setServiceType("Prometheus2");
+        inv.setConfiguration("{}");
+        inv.setDescription("description");
         event.setInventory(inv);
         return event;
     }

Comment on lines +14 to +18
@RestRequest(
path = "/external/service/configuration/{uuid}",
isAction = true,
method = HttpMethod.PUT,
responseClass = APIUpdateExternalServiceConfigurationEvent.class
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

REST 路径应使用资源复数形式。 当前 path 使用单数 configuration,与 API 命名规范不一致,建议改为资源复数以保持一致性。
(依据编码规范)

🔧 建议修改
-        path = "/external/service/configuration/{uuid}",
+        path = "/external/service/configurations/{uuid}",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@RestRequest(
path = "/external/service/configuration/{uuid}",
isAction = true,
method = HttpMethod.PUT,
responseClass = APIUpdateExternalServiceConfigurationEvent.class
`@RestRequest`(
path = "/external/service/configurations/{uuid}",
isAction = true,
method = HttpMethod.PUT,
responseClass = APIUpdateExternalServiceConfigurationEvent.class
🤖 Prompt for AI Agents
In
`@header/src/main/java/org/zstack/header/core/external/service/APIUpdateExternalServiceConfigurationMsg.java`
around lines 14 - 18, The REST path in the RestRequest annotation on
APIUpdateExternalServiceConfigurationMsg uses the singular "configuration";
change the path value to use the plural resource form (e.g., "configurations")
in the `@RestRequest` on APIUpdateExternalServiceConfigurationMsg so it follows
the API naming convention, and update any callers or documentation that
reference the old path string to the new
"/external/service/configurations/{uuid}" if present.

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.

6 participants