Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 32 additions & 12 deletions conf/db/zsv/V5.1.0__schema.sql
Original file line number Diff line number Diff line change
@@ -1,12 +1,32 @@
CREATE TABLE IF NOT EXISTS `zstack`.`TpmKeyBackupVO` (
`uuid` char(32) NOT NULL UNIQUE,
`lastOpDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`createDate` timestamp NOT NULL DEFAULT '1999-12-31 23:59:59',
PRIMARY KEY (`uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DELETE FROM `EncryptedResourceKeyRefVO`
WHERE `resourceUuid` NOT IN (SELECT `uuid` FROM `ResourceVO`);
ALTER TABLE `EncryptedResourceKeyRefVO`
ADD CONSTRAINT `fkEncryptedResourceKeyRefResourceVO` FOREIGN KEY (`resourceUuid`) REFERENCES `ResourceVO`(`uuid`)
ON DELETE CASCADE;
CREATE TABLE IF NOT EXISTS `zstack`.`TpmKeyBackupVO` (
`uuid` char(32) NOT NULL UNIQUE,
`lastOpDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`createDate` timestamp NOT NULL DEFAULT '1999-12-31 23:59:59',
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

createDate 使用固定历史时间会导致数据时间语义失真。

这里把 createDate 默认值写死为 1999-12-31 23:59:59,会让新插入记录的创建时间不准确,影响审计、排序和排障。建议改为 DEFAULT CURRENT_TIMESTAMP

💡建议修改
-    `createDate` timestamp NOT NULL DEFAULT '1999-12-31 23:59:59',
+    `createDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,

As per coding guidelines **/*.sql: “Do not use DEFAULT 0000-00-00 00:00:00 , use DEFAULT CURRENT_TIMESTAMP instead”.

📝 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
`createDate` timestamp NOT NULL DEFAULT '1999-12-31 23:59:59',
`createDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@conf/db/zsv/V5.1.0__schema.sql` at line 4, The column createDate currently
uses a hard-coded default ('1999-12-31 23:59:59'), which breaks time semantics;
update the column definition for createDate in the migration (the `createDate`
timestamp NOT NULL line) to use a dynamic default by replacing the literal with
DEFAULT CURRENT_TIMESTAMP while preserving NOT NULL, and ensure any subsequent
DDL in this migration that relies on that column uses the new CURRENT_TIMESTAMP
behavior.

PRIMARY KEY (`uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DELETE FROM `EncryptedResourceKeyRefVO`
WHERE `resourceUuid` NOT IN (SELECT `uuid` FROM `ResourceVO`);
ALTER TABLE `EncryptedResourceKeyRefVO`
ADD CONSTRAINT `fkEncryptedResourceKeyRefResourceVO` FOREIGN KEY (`resourceUuid`) REFERENCES `ResourceVO`(`uuid`)
ON DELETE CASCADE;

-- Feature: ZCenter Account | ZSV-12257

ALTER TABLE `zstack`.`AccountVO`
ADD COLUMN `source` varchar(32) NOT NULL DEFAULT 'Local' AFTER `type`;

UPDATE `zstack`.`AccountVO` a
INNER JOIN `zstack`.`AccountThirdPartyAccountSourceRefVO` ref ON ref.accountUuid = a.uuid
INNER JOIN `zstack`.`LdapServerVO` ldap ON ldap.uuid = ref.accountSourceUuid
SET a.`source` = IF(ldap.serverType IN ('OpenLdap', 'WindowsAD'), ldap.serverType, 'WindowsAD');

UPDATE `zstack`.`AccountVO` a
INNER JOIN `zstack`.`AccountThirdPartyAccountSourceRefVO` ref ON ref.accountUuid = a.uuid
INNER JOIN `zstack`.`ThirdPartyAccountSourceVO` src ON src.uuid = ref.accountSourceUuid
SET a.`source` = src.type
WHERE src.type IN ('CAS', 'OAuth2');

UPDATE `zstack`.`AccountVO`
SET `type` = 'Normal'
WHERE `type` = 'ThirdParty';
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;

@Inventory(mappingVOClass = AccountVO.class)
@ExpandedQueries({
Expand All @@ -23,6 +22,7 @@ public class AccountInventory {
private String name;
private String description;
private String type;
private String source;
private String state;
private Timestamp createDate;
private Timestamp lastOpDate;
Expand All @@ -33,6 +33,7 @@ public static AccountInventory valueOf(AccountVO vo) {
inv.setName(vo.getName());
inv.setDescription(vo.getDescription());
inv.setType(vo.getType().toString());
inv.setSource(vo.getSource().toString());
inv.setState(vo.getState().toString());
inv.setCreateDate(vo.getCreateDate());
inv.setLastOpDate(vo.getLastOpDate());
Expand All @@ -55,6 +56,14 @@ public void setType(String type) {
this.type = type;
}

public String getSource() {
return source;
}

public void setSource(String source) {
this.source = source;
}

public String getDescription() {
return description;
}
Expand Down Expand Up @@ -109,6 +118,7 @@ public static AccountInventory __example__() {
account.setName("account1");
account.setDescription("account1-description");
account.setType(AccountType.Normal.toString());
account.setSource(AccountSource.Local.toString());
account.setState(AccountState.Enabled.toString());
account.setCreateDate(new Timestamp(DocUtils.date));
account.setLastOpDate(new Timestamp(DocUtils.date));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ doc {
type "String"
since "4.0.0"
}
field {
name "source"
desc "账户来源,创建时确定且不可修改"
type "String"
since "5.1.0"
}
field {
name "state"
desc "账户状态"
Expand Down
25 changes: 25 additions & 0 deletions header/src/main/java/org/zstack/header/identity/AccountSource.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.zstack.header.identity;

import org.zstack.header.configuration.PythonClass;

import javax.annotation.Nullable;

/**
* Where an account was originally created. Immutable after creation (ZSV-12257).
*/
@PythonClass
public enum AccountSource {
Local,
OpenLdap,
WindowsAD,
CAS,
OAuth2,
ZCenter;

public static AccountSource fromLdapServerTypeName(@Nullable String serverType) {
if (OpenLdap.name().equals(serverType)) {
return OpenLdap;
}
return WindowsAD;
}
Comment on lines +19 to +24
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 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 | ⚡ Quick win

避免将未知/空 LDAP 类型静默映射为 WindowsAD

当前逻辑会把 null 或任意未知 serverType 都写成 WindowsAD,会造成来源字段误标。建议显式匹配 WindowsAD,其余输入直接失败快。

建议修改
 public static AccountSource fromLdapServerTypeName(`@Nullable` String serverType) {
-    if (OpenLdap.name().equals(serverType)) {
-        return OpenLdap;
-    }
-    return WindowsAD;
+    if (OpenLdap.name().equals(serverType)) {
+        return OpenLdap;
+    }
+    if (WindowsAD.name().equals(serverType)) {
+        return WindowsAD;
+    }
+    throw new IllegalArgumentException(String.format("Unsupported LDAP server type: %s", serverType));
 }
📝 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 AccountSource fromLdapServerTypeName(@Nullable String serverType) {
if (OpenLdap.name().equals(serverType)) {
return OpenLdap;
}
return WindowsAD;
}
public static AccountSource fromLdapServerTypeName(`@Nullable` String serverType) {
if (OpenLdap.name().equals(serverType)) {
return OpenLdap;
}
if (WindowsAD.name().equals(serverType)) {
return WindowsAD;
}
throw new IllegalArgumentException(String.format("Unsupported LDAP server type: %s", serverType));
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@header/src/main/java/org/zstack/header/identity/AccountSource.java` around
lines 19 - 24, The method AccountSource.fromLdapServerTypeName currently maps
any null or unknown serverType to WindowsAD causing silent mislabeling; change
it to explicitly handle known values only by comparing serverType to
OpenLdap.name() and WindowsAD.name(), and throw an IllegalArgumentException (or
return null if preferred by project convention) when serverType is null or does
not match either OpenLdap or WindowsAD so unknown inputs fail fast instead of
being mapped to WindowsAD; update the method
AccountSource.fromLdapServerTypeName to perform those explicit checks and raise
the error using clear messaging that includes the original serverType.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Comment from wenhao.zhang:

在 LDAP 部分传入的值,不是 null 也是 "Unknown",这里没办法只能塞进 WindowsAD 中,和现有的默认值保持一致

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@zstack-robot-1,感谢 wenhao.zhang 的说明。了解了——由于 LDAP 侧实际只会传入 null"Unknown",回退到 WindowsAD 是对齐现有默认值的有意设计,不撤回之前的建议,但不再坚持。

为了让后续维护者更容易理解这一回退语义,建议在 return WindowsAD; 前加一行简短注释,例如:

// null or "Unknown" serverType defaults to WindowsAD to align with legacy behavior
return WindowsAD;

这不影响任何逻辑,只是让意图更自解释。采纳与否由团队决定。

(´• ω •`) 🐇


✏️ Learnings added
Learnt from: zstack-robot-1
Repo: MatheMatrix/zstack PR: 4018
File: header/src/main/java/org/zstack/header/identity/AccountSource.java:19-24
Timestamp: 2026-05-19T09:46:14.609Z
Learning: 在 `AccountSource.fromLdapServerTypeName(Nullable String serverType)` 方法中(`header/src/main/java/org/zstack/header/identity/AccountSource.java`),将 `null` 或 `"Unknown"` 的 LDAP serverType 回退映射到 `WindowsAD` 是有意为之的行为,与项目现有默认值保持一致。代码审查中不应将此视为静默误标问题。

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
Repo: MatheMatrix/zstack PR: 3826
File: compute/src/main/java/org/zstack/compute/allocator/HostAllocatorChain.java:261-265
Timestamp: 2026-04-22T04:38:00.864Z
Learning: 在 ZStack 的 `Platform.err()` / `Platform.toRawString()` 调用中,传入用于格式化的 `%s` 参数若实现了 `org.zstack.header.core.I18nMessage`,框架会自动通过 `getDetails()` 取得原始文本、并通过 `getI18nDetails()` 取得国际化文本。因此,在代码审查中不应将“直接把实现了 `I18nMessage` 的对象作为 `%s` 参数传入而可能打印出对象地址/不安全”当作问题;此用法是正确且应当被视为安全的(例如将 `HostCandidate.RejectedCandidate` 这类实现了 `I18nMessage` 的对象作为 `%s` 参数传入)。

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@
public enum AccountType {
SystemAdmin,
Normal,
/**
* @deprecated Use {@link AccountType#Normal} with {@link AccountSource} instead (ZSV-12257).
*/
@Deprecated
ThirdParty
}
12 changes: 12 additions & 0 deletions header/src/main/java/org/zstack/header/identity/AccountVO.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public class AccountVO extends ResourceVO {
@Enumerated(EnumType.STRING)
private AccountType type;

@Column(nullable = false)
@Enumerated(EnumType.STRING)
private AccountSource source = AccountSource.Local;

@Column
@Enumerated(EnumType.STRING)
private AccountState state;
Expand Down Expand Up @@ -72,6 +76,14 @@ public void setType(AccountType type) {
this.type = type;
}

public AccountSource getSource() {
return source;
}

public void setSource(AccountSource source) {
this.source = source;
}

public AccountState getState() {
return state;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class AccountVO_ extends ResourceVO_ {
public static volatile SingularAttribute<AccountVO, String> description;
public static volatile SingularAttribute<AccountVO, String> password;
public static volatile SingularAttribute<AccountVO, AccountType> type;
public static volatile SingularAttribute<AccountVO, AccountSource> source;
public static volatile SingularAttribute<AccountVO, AccountState> state;
public static volatile SingularAttribute<AccountVO, Timestamp> createDate;
public static volatile SingularAttribute<AccountVO, Timestamp> lastOpDate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public class CreateAccountMsg extends NeedReplyMessage {
@NoLogging
private String password;
private String type;
private String source = AccountSource.Local.toString();
private String description;
private AccountState state;

Expand Down Expand Up @@ -48,6 +49,14 @@ public void setType(String type) {
this.type = type;
}

public String getSource() {
return source;
}

public void setSource(String source) {
this.source = source;
}

public String getDescription() {
return description;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ protected void scripts() {
vo.setName(AccountConstant.INITIAL_SYSTEM_ADMIN_NAME);
vo.setPassword(AccountConstant.INITIAL_SYSTEM_ADMIN_PASSWORD);
vo.setType(AccountType.SystemAdmin);
vo.setSource(AccountSource.Local);
vo.setState(AccountState.Enabled);
persist(vo);
flush();
Expand Down Expand Up @@ -533,7 +534,21 @@ protected AccountInventory scripts() {
vo.setName(msg.getName());
vo.setDescription(msg.getDescription());
vo.setPassword(msg.getPassword());
vo.setType(msg.getType() != null ? AccountType.valueOf(msg.getType()) : AccountType.Normal);
AccountType accountType = msg.getType() != null ? AccountType.valueOf(msg.getType()) : AccountType.Normal;
if (accountType == AccountType.ThirdParty) {
throw operr("account type[ThirdParty] is deprecated; use Normal with account source instead")
.toException();
}
vo.setType(accountType);

try {
vo.setSource(AccountSource.valueOf(msg.getSource()));
} catch (IllegalArgumentException e) {
throw operr("invalid account source[%s]", msg.getSource())
.withOpaque("allowed.values", list(AccountSource.values()))
.toException();
}

vo.setState(msg.getState() == null ? AccountState.Enabled : msg.getState());
persist(vo);
reload(vo);
Expand Down Expand Up @@ -1005,6 +1020,11 @@ private void validate(APIChangeAccountTypeMsg msg) {
));
}

if (AccountType.ThirdParty.toString().equals(msg.getType())) {
throw new ApiMessageInterceptionException(argerr(
"account type[ThirdParty] is deprecated; use Normal with account source instead"));
}

if (!AccountType.SystemAdmin.toString().equals(msg.getType())) {
throw new ApiMessageInterceptionException(argerr(
"Only promoting to SystemAdmin is currently supported, got type[%s].", msg.getType()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ public void prepareDbInitialValue() {
if (AccountConstant.OTHER_ROLE_UUID.equals(role.getUuid())
|| AccountConstant.LEGACY_ROLE_UUID.equals(role.getUuid())) {
List<String> accountUuidList = Q.New(AccountVO.class)
.in(AccountVO_.type, list(AccountType.Normal, AccountType.ThirdParty))
.eq(AccountVO_.type, AccountType.Normal)
.select(AccountVO_.uuid)
.listValues();
Comment on lines 179 to 182
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

type=Normal 过滤在迁移后会误纳入第三方来源账户

Line 180 现在只按 AccountType.Normal 过滤;但本次迁移会把历史第三方账户也转成 Normal,这会让这些账户重新被纳入预置角色初始化,和“排除第三方账户”的目标冲突。建议在这里增加 source 条件做排除(例如仅纳入本地来源账户)。

建议修复(示意)
                     List<String> accountUuidList = Q.New(AccountVO.class)
-                            .in(AccountVO_.type, AccountType.Normal)
+                            .eq(AccountVO_.type, AccountType.Normal)
+                            .eq(AccountVO_.source, AccountSource.Local)
                             .select(AccountVO_.uuid)
                             .listValues();
📝 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
List<String> accountUuidList = Q.New(AccountVO.class)
.in(AccountVO_.type, list(AccountType.Normal, AccountType.ThirdParty))
.in(AccountVO_.type, AccountType.Normal)
.select(AccountVO_.uuid)
.listValues();
List<String> accountUuidList = Q.New(AccountVO.class)
.eq(AccountVO_.type, AccountType.Normal)
.eq(AccountVO_.source, AccountSource.Local)
.select(AccountVO_.uuid)
.listValues();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@identity/src/main/java/org/zstack/identity/rbac/RBACManagerImpl.java` around
lines 179 - 182, The current query in RBACManagerImpl that builds
accountUuidList uses only AccountVO_.type == AccountType.Normal, which will
include migrated third‑party accounts now marked as Normal; update the query to
also filter by account source to include only local/native accounts (e.g., add a
condition on AccountVO_.source == <local-source-constant> or the enum/constant
used for local sources) so third‑party-sourced accounts are excluded from preset
role initialization; reference AccountVO, AccountVO_.type, AccountVO_.source,
and AccountType.Normal when making this change.

for (String accountUuid : accountUuidList) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.zstack.identity.imports.header;

import org.zstack.header.identity.AccountSource;
import org.zstack.identity.imports.entity.SyncCreatedAccountStrategy;
import org.zstack.identity.imports.entity.SyncUpdateAccountStateStrategy;

Expand All @@ -12,6 +13,7 @@
public class ImportAccountSpec {
private String sourceUuid;
private String sourceType;
private AccountSource accountSource;
public List<ImportAccountItem> accountList = new ArrayList<>();
private boolean createIfNotExist = true;
private SyncCreatedAccountStrategy syncCreateStrategy = SyncCreatedAccountStrategy.NoAction;
Expand All @@ -33,6 +35,14 @@ public void setSourceType(String sourceType) {
this.sourceType = sourceType;
}

public AccountSource getAccountSource() {
return accountSource;
}

public void setAccountSource(AccountSource accountSource) {
this.accountSource = accountSource;
}

public List<ImportAccountItem> getAccountList() {
return accountList;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.zstack.header.core.WhileCompletion;
import org.zstack.header.errorcode.ErrorCode;
import org.zstack.header.identity.AccountInventory;
import org.zstack.header.identity.AccountSource;
import org.zstack.header.identity.AccountState;
import org.zstack.header.identity.AccountType;
import org.zstack.header.identity.AccountVO;
Expand Down Expand Up @@ -188,6 +189,7 @@ private void buildContextsFromSpec() {
for (ImportAccountItem accountSpec : spec.getAccountList()) {
final ImportThirdPartyAccountContext context = new ImportThirdPartyAccountContext();
context.spec = accountSpec;
context.accountSource = spec.getAccountSource();
contexts.add(context);
validContexts.add(context);
}
Expand Down Expand Up @@ -260,6 +262,7 @@ private void fillAccountIfExists() {
AccountVO account = uuidExistingAccountMap.get(accountUuid);
if (account != null) {
context.account = AccountInventory.valueOf(account);
context.accountSource = spec.getAccountSource();
context.accountExisting = true;
}
}
Expand Down Expand Up @@ -344,11 +347,25 @@ private void createAccount(ImportThirdPartyAccountContext context, WhileCompleti
"account[uuid=%s] newlyCreate stateInAccountSource=%s stateUpdateTo=%s %s",
accountUuid, stateInAccountSource, stateUpdateTo, stateMachine));

AccountType accountType = context.spec.getAccountType() != null ?
context.spec.getAccountType() : AccountType.Normal;
if (accountType == AccountType.ThirdParty) {
accountType = AccountType.Normal;
}
if (context.accountSource == null) {
context.errorForAccountExecution = operr(
"account source is required when importing account[name=%s]", context.spec.getUsername());
validContexts.remove(context);
whileCompletion.done();
return;
}

CreateAccountMsg message = new CreateAccountMsg();
message.setUuid(accountUuid);
message.setName(context.spec.getUsername());
message.setPassword(generatePassword());
message.setType(context.spec.getAccountType().toString());
message.setType(accountType.toString());
message.setSource(context.accountSource.toString());
message.setState(stateUpdateTo);

bus.makeTargetServiceIdByResourceUuid(message, AccountConstant.SERVICE_ID, accountUuid);
Expand Down Expand Up @@ -793,26 +810,18 @@ public void run(FlowTrigger trigger, Map data) {

@Override
public void run(FlowTrigger trigger, Map data) {
long totalSize = Q.New(AccountThirdPartyAccountSourceRefVO.class)
.eq(AccountThirdPartyAccountSourceRefVO_.accountSourceUuid, self.getUuid())
.count();

// All related accounts with "ThirdParty" type will be destroyed
SQL.New("select " +
"account.uuid " +
"from " +
"AccountThirdPartyAccountSourceRefVO ref, " +
"AccountVO account " +
"where " +
"ref.accountSourceUuid = :sourceUuid and " +
"ref.accountUuid = account.uuid and " +
"account.type = :accountType",
String.class)
.param("sourceUuid", self.getUuid())
.param("accountType", AccountType.ThirdParty)
.limit(300)
.paginate(totalSize, (List<String> accountUuids) -> accountUuidList.addAll(accountUuids));
trigger.next();
gatherAccountsForDestroySource(new ReturnValueCompletion<List<String>>(trigger) {
@Override
public void success(List<String> returnValue) {
accountUuidList.addAll(returnValue);
trigger.next();
}

@Override
public void fail(ErrorCode errorCode) {
trigger.fail(errorCode);
}
});
}
}).then(new NoRollbackFlow() {
String __name__ = "destroy-source";
Expand Down Expand Up @@ -904,4 +913,28 @@ public void handle(ErrorCode errCode, Map data) {
}

protected abstract void destroySource(Completion completion);

protected void gatherAccountsForDestroySource(ReturnValueCompletion<List<String>> completion) {
long totalSize = Q.New(AccountThirdPartyAccountSourceRefVO.class)
.eq(AccountThirdPartyAccountSourceRefVO_.accountSourceUuid, self.getUuid())
.count();

List<String> results = new ArrayList<>((int) totalSize);
// Imported accounts (non-local source) bound to this source will be destroyed
SQL.New("select " +
"account.uuid " +
"from " +
"AccountThirdPartyAccountSourceRefVO ref, " +
"AccountVO account " +
"where " +
"ref.accountSourceUuid = :sourceUuid and " +
"ref.accountUuid = account.uuid and " +
"account.source != :localSource",
String.class)
.param("sourceUuid", self.getUuid())
.param("localSource", AccountSource.Local)
.limit(300)
.paginate(totalSize, (List<String> accountUuids) -> results.addAll(accountUuids));
completion.success(results);
}
}
Loading