GutoVeronezi commented on code in PR #8307:
URL: https://github.com/apache/cloudstack/pull/8307#discussion_r1479862147


##########
framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java:
##########
@@ -143,52 +148,68 @@ public boolean stop() {
     @Override
     public void checkAndSendQuotaAlertEmails() {
         List<DeferredQuotaEmail> deferredQuotaEmailList = new 
ArrayList<DeferredQuotaEmail>();
-        final BigDecimal zeroBalance = new BigDecimal(0);
+
+        s_logger.info("Checking and sending quota alert emails.");
         for (final QuotaAccountVO quotaAccount : 
_quotaAcc.listAllQuotaAccount()) {
-            if (s_logger.isDebugEnabled()) {
-                s_logger.debug("checkAndSendQuotaAlertEmails accId=" + 
quotaAccount.getId());
-            }
-            BigDecimal accountBalance = quotaAccount.getQuotaBalance();
-            Date balanceDate = quotaAccount.getQuotaBalanceDate();
-            Date alertDate = quotaAccount.getQuotaAlertDate();
-            int lockable = quotaAccount.getQuotaEnforce();
-            BigDecimal thresholdBalance = quotaAccount.getQuotaMinBalance();
-            if (accountBalance != null) {
-                AccountVO account = _accountDao.findById(quotaAccount.getId());
-                if (account == null) {
-                    continue; // the account is removed
-                }
-                if (s_logger.isDebugEnabled()) {
-                    s_logger.debug("checkAndSendQuotaAlertEmails: Check id=" + 
account.getId() + " bal=" + accountBalance + ", alertDate=" + alertDate + ", 
lockable=" + lockable);
-                }
-                if (accountBalance.compareTo(zeroBalance) < 0) {
-                    if (_lockAccountEnforcement && (lockable == 1)) {
-                        if (_quotaManager.isLockable(account)) {
-                            s_logger.info("Locking account " + 
account.getAccountName() + " due to quota < 0.");
-                            lockAccount(account.getId());
-                        }
-                    }
-                    if (alertDate == null || (balanceDate.after(alertDate) && 
getDifferenceDays(alertDate, new Date()) > 1)) {
-                        s_logger.info("Sending alert " + 
account.getAccountName() + " due to quota < 0.");
-                        deferredQuotaEmailList.add(new 
DeferredQuotaEmail(account, quotaAccount, 
QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY));
-                    }
-                } else if (accountBalance.compareTo(thresholdBalance) < 0) {
-                    if (alertDate == null || (balanceDate.after(alertDate) && 
getDifferenceDays(alertDate, new Date()) > 1)) {
-                        s_logger.info("Sending alert " + 
account.getAccountName() + " due to quota below threshold.");
-                        deferredQuotaEmailList.add(new 
DeferredQuotaEmail(account, quotaAccount, 
QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW));
-                    }
-                }
-            }
+            checkQuotaAlertEmailForAccount(deferredQuotaEmailList, 
quotaAccount);
         }
 
         for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
-            if (s_logger.isDebugEnabled()) {
-                s_logger.debug("checkAndSendQuotaAlertEmails: Attempting to 
send quota alert email to users of account: " + 
emailToBeSent.getAccount().getAccountName());
-            }
+            s_logger.debug(String.format("Attempting to send a quota alert 
email to users of account [%s].", emailToBeSent.getAccount().getAccountName()));
             sendQuotaAlert(emailToBeSent);
         }
     }
 
+    /**
+     * Checks a given quota account to see if they should receive any emails. 
First by checking if it has any balance at all, if its account can be found, 
then checks
+     * if they should receive either QUOTA_EMPTY or QUOTA_LOW emails, taking 
into account if these email templates are disabled or not for that account.
+     * */
+    protected void checkQuotaAlertEmailForAccount(List<DeferredQuotaEmail> 
deferredQuotaEmailList, QuotaAccountVO quotaAccount) {

Review Comment:
   This method is quite long, we could break it in small pieces.



##########
framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailConfigurationDaoImpl.java:
##########
@@ -0,0 +1,132 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.quota.dao;
+
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.JoinBuilder;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.TransactionCallback;
+import com.cloud.utils.db.TransactionCallbackNoReturn;
+import com.cloud.utils.db.TransactionLegacy;
+import com.cloud.utils.db.TransactionStatus;
+import org.apache.cloudstack.quota.constant.QuotaConfig;
+import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
+import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+import java.util.List;
+
+@Component
+public class QuotaEmailConfigurationDaoImpl extends 
GenericDaoBase<QuotaEmailConfigurationVO, Long> implements 
QuotaEmailConfigurationDao {
+
+    @Inject
+    private QuotaEmailTemplatesDao quotaEmailTemplatesDao;
+
+    private SearchBuilder<QuotaEmailConfigurationVO> searchBuilderFindByIds;
+
+    private SearchBuilder<QuotaEmailTemplatesVO> 
searchBuilderFindByTemplateName;
+
+    private SearchBuilder<QuotaEmailConfigurationVO> 
searchBuilderFindByTemplateTypeAndAccountId;
+
+    @PostConstruct
+    public void init() {
+        searchBuilderFindByIds = createSearchBuilder();
+        searchBuilderFindByIds.and("account_id", 
searchBuilderFindByIds.entity().getAccountId(), SearchCriteria.Op.EQ);
+        searchBuilderFindByIds.and("email_template_id", 
searchBuilderFindByIds.entity().getEmailTemplateId(), SearchCriteria.Op.EQ);
+        searchBuilderFindByIds.done();
+
+        searchBuilderFindByTemplateName = 
quotaEmailTemplatesDao.createSearchBuilder();
+        searchBuilderFindByTemplateName.and("template_name", 
searchBuilderFindByTemplateName.entity().getTemplateName(), 
SearchCriteria.Op.EQ);
+
+        searchBuilderFindByTemplateTypeAndAccountId = createSearchBuilder();
+        searchBuilderFindByTemplateTypeAndAccountId.and("account_id", 
searchBuilderFindByTemplateTypeAndAccountId.entity().getAccountId(), 
SearchCriteria.Op.EQ);
+        searchBuilderFindByTemplateTypeAndAccountId.join("email_template_id", 
searchBuilderFindByTemplateName, 
searchBuilderFindByTemplateName.entity().getId(),
+                
searchBuilderFindByTemplateTypeAndAccountId.entity().getEmailTemplateId(), 
JoinBuilder.JoinType.INNER);
+
+        searchBuilderFindByTemplateName.done();
+        searchBuilderFindByTemplateTypeAndAccountId.done();
+    }
+
+    @Override
+    public QuotaEmailConfigurationVO findByAccountIdAndEmailTemplateId(long 
accountId, long emailTemplateId) {
+        SearchCriteria<QuotaEmailConfigurationVO> sc = 
searchBuilderFindByIds.create();
+        sc.setParameters("account_id", accountId);
+        sc.setParameters("email_template_id", emailTemplateId);
+        return Transaction.execute(TransactionLegacy.USAGE_DB, new 
TransactionCallback<QuotaEmailConfigurationVO>() {
+            @Override
+            public QuotaEmailConfigurationVO doInTransaction(TransactionStatus 
status) {
+                return findOneBy(sc);
+            }
+        });

Review Comment:
   IMO, we could increment readability here with lambda.



##########
framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailConfigurationDaoImpl.java:
##########
@@ -0,0 +1,132 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.quota.dao;
+
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.JoinBuilder;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.TransactionCallback;
+import com.cloud.utils.db.TransactionCallbackNoReturn;
+import com.cloud.utils.db.TransactionLegacy;
+import com.cloud.utils.db.TransactionStatus;
+import org.apache.cloudstack.quota.constant.QuotaConfig;
+import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
+import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+import java.util.List;
+
+@Component
+public class QuotaEmailConfigurationDaoImpl extends 
GenericDaoBase<QuotaEmailConfigurationVO, Long> implements 
QuotaEmailConfigurationDao {
+
+    @Inject
+    private QuotaEmailTemplatesDao quotaEmailTemplatesDao;
+
+    private SearchBuilder<QuotaEmailConfigurationVO> searchBuilderFindByIds;
+
+    private SearchBuilder<QuotaEmailTemplatesVO> 
searchBuilderFindByTemplateName;
+
+    private SearchBuilder<QuotaEmailConfigurationVO> 
searchBuilderFindByTemplateTypeAndAccountId;
+
+    @PostConstruct
+    public void init() {
+        searchBuilderFindByIds = createSearchBuilder();
+        searchBuilderFindByIds.and("account_id", 
searchBuilderFindByIds.entity().getAccountId(), SearchCriteria.Op.EQ);
+        searchBuilderFindByIds.and("email_template_id", 
searchBuilderFindByIds.entity().getEmailTemplateId(), SearchCriteria.Op.EQ);
+        searchBuilderFindByIds.done();
+
+        searchBuilderFindByTemplateName = 
quotaEmailTemplatesDao.createSearchBuilder();
+        searchBuilderFindByTemplateName.and("template_name", 
searchBuilderFindByTemplateName.entity().getTemplateName(), 
SearchCriteria.Op.EQ);
+
+        searchBuilderFindByTemplateTypeAndAccountId = createSearchBuilder();
+        searchBuilderFindByTemplateTypeAndAccountId.and("account_id", 
searchBuilderFindByTemplateTypeAndAccountId.entity().getAccountId(), 
SearchCriteria.Op.EQ);
+        searchBuilderFindByTemplateTypeAndAccountId.join("email_template_id", 
searchBuilderFindByTemplateName, 
searchBuilderFindByTemplateName.entity().getId(),
+                
searchBuilderFindByTemplateTypeAndAccountId.entity().getEmailTemplateId(), 
JoinBuilder.JoinType.INNER);
+
+        searchBuilderFindByTemplateName.done();
+        searchBuilderFindByTemplateTypeAndAccountId.done();
+    }
+
+    @Override
+    public QuotaEmailConfigurationVO findByAccountIdAndEmailTemplateId(long 
accountId, long emailTemplateId) {
+        SearchCriteria<QuotaEmailConfigurationVO> sc = 
searchBuilderFindByIds.create();
+        sc.setParameters("account_id", accountId);
+        sc.setParameters("email_template_id", emailTemplateId);
+        return Transaction.execute(TransactionLegacy.USAGE_DB, new 
TransactionCallback<QuotaEmailConfigurationVO>() {
+            @Override
+            public QuotaEmailConfigurationVO doInTransaction(TransactionStatus 
status) {
+                return findOneBy(sc);
+            }
+        });
+    }
+
+    @Override
+    public QuotaEmailConfigurationVO 
updateQuotaEmailConfiguration(QuotaEmailConfigurationVO 
quotaEmailConfigurationVO) {
+        SearchCriteria<QuotaEmailConfigurationVO> sc = 
searchBuilderFindByIds.create();
+        sc.setParameters("account_id", 
quotaEmailConfigurationVO.getAccountId());
+        sc.setParameters("email_template_id", 
quotaEmailConfigurationVO.getEmailTemplateId());
+        Transaction.execute(TransactionLegacy.USAGE_DB, new 
TransactionCallbackNoReturn() {
+            @Override
+            public void doInTransactionWithoutResult(TransactionStatus status) 
{
+                update(quotaEmailConfigurationVO, sc);
+            }
+        });
+
+        return quotaEmailConfigurationVO;
+    }
+
+    @Override
+    public void persistQuotaEmailConfiguration(QuotaEmailConfigurationVO 
quotaEmailConfigurationVO) {
+        Transaction.execute(TransactionLegacy.USAGE_DB, new 
TransactionCallbackNoReturn() {
+            @Override
+            public void doInTransactionWithoutResult(TransactionStatus status) 
{
+                persist(quotaEmailConfigurationVO);
+            }
+        });
+    }
+
+    @Override
+    public List<QuotaEmailConfigurationVO> listByAccount(long accountId) {
+        SearchCriteria<QuotaEmailConfigurationVO> sc = 
searchBuilderFindByIds.create();
+        sc.setParameters("account_id", accountId);
+
+        return Transaction.execute(TransactionLegacy.USAGE_DB, new 
TransactionCallback<List<QuotaEmailConfigurationVO>>() {
+            @Override
+            public List<QuotaEmailConfigurationVO> 
doInTransaction(TransactionStatus status) {
+                return listBy(sc);
+            }
+        });
+    }
+
+    @Override
+    public QuotaEmailConfigurationVO findByAccountIdAndEmailTemplateType(long 
accountId, QuotaConfig.QuotaEmailTemplateTypes quotaEmailTemplateType) {
+        SearchCriteria<QuotaEmailConfigurationVO> sc = 
searchBuilderFindByTemplateTypeAndAccountId.create();
+        sc.setParameters("account_id", accountId);
+        sc.setJoinParameters("email_template_id", "template_name", 
quotaEmailTemplateType.toString());
+
+        return Transaction.execute(TransactionLegacy.USAGE_DB, new 
TransactionCallback<QuotaEmailConfigurationVO>() {
+            @Override
+            public QuotaEmailConfigurationVO doInTransaction(TransactionStatus 
status) {
+                return findOneBy(sc);
+            }
+        });

Review Comment:
   IMO, we could increment readability here with lambda.



##########
framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailConfigurationDaoImpl.java:
##########
@@ -0,0 +1,132 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.quota.dao;
+
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.JoinBuilder;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.TransactionCallback;
+import com.cloud.utils.db.TransactionCallbackNoReturn;
+import com.cloud.utils.db.TransactionLegacy;
+import com.cloud.utils.db.TransactionStatus;
+import org.apache.cloudstack.quota.constant.QuotaConfig;
+import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
+import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+import java.util.List;
+
+@Component
+public class QuotaEmailConfigurationDaoImpl extends 
GenericDaoBase<QuotaEmailConfigurationVO, Long> implements 
QuotaEmailConfigurationDao {
+
+    @Inject
+    private QuotaEmailTemplatesDao quotaEmailTemplatesDao;
+
+    private SearchBuilder<QuotaEmailConfigurationVO> searchBuilderFindByIds;
+
+    private SearchBuilder<QuotaEmailTemplatesVO> 
searchBuilderFindByTemplateName;
+
+    private SearchBuilder<QuotaEmailConfigurationVO> 
searchBuilderFindByTemplateTypeAndAccountId;
+
+    @PostConstruct
+    public void init() {
+        searchBuilderFindByIds = createSearchBuilder();
+        searchBuilderFindByIds.and("account_id", 
searchBuilderFindByIds.entity().getAccountId(), SearchCriteria.Op.EQ);
+        searchBuilderFindByIds.and("email_template_id", 
searchBuilderFindByIds.entity().getEmailTemplateId(), SearchCriteria.Op.EQ);
+        searchBuilderFindByIds.done();
+
+        searchBuilderFindByTemplateName = 
quotaEmailTemplatesDao.createSearchBuilder();
+        searchBuilderFindByTemplateName.and("template_name", 
searchBuilderFindByTemplateName.entity().getTemplateName(), 
SearchCriteria.Op.EQ);
+
+        searchBuilderFindByTemplateTypeAndAccountId = createSearchBuilder();
+        searchBuilderFindByTemplateTypeAndAccountId.and("account_id", 
searchBuilderFindByTemplateTypeAndAccountId.entity().getAccountId(), 
SearchCriteria.Op.EQ);
+        searchBuilderFindByTemplateTypeAndAccountId.join("email_template_id", 
searchBuilderFindByTemplateName, 
searchBuilderFindByTemplateName.entity().getId(),
+                
searchBuilderFindByTemplateTypeAndAccountId.entity().getEmailTemplateId(), 
JoinBuilder.JoinType.INNER);
+
+        searchBuilderFindByTemplateName.done();
+        searchBuilderFindByTemplateTypeAndAccountId.done();
+    }
+
+    @Override
+    public QuotaEmailConfigurationVO findByAccountIdAndEmailTemplateId(long 
accountId, long emailTemplateId) {
+        SearchCriteria<QuotaEmailConfigurationVO> sc = 
searchBuilderFindByIds.create();
+        sc.setParameters("account_id", accountId);
+        sc.setParameters("email_template_id", emailTemplateId);
+        return Transaction.execute(TransactionLegacy.USAGE_DB, new 
TransactionCallback<QuotaEmailConfigurationVO>() {
+            @Override
+            public QuotaEmailConfigurationVO doInTransaction(TransactionStatus 
status) {
+                return findOneBy(sc);
+            }
+        });
+    }
+
+    @Override
+    public QuotaEmailConfigurationVO 
updateQuotaEmailConfiguration(QuotaEmailConfigurationVO 
quotaEmailConfigurationVO) {
+        SearchCriteria<QuotaEmailConfigurationVO> sc = 
searchBuilderFindByIds.create();
+        sc.setParameters("account_id", 
quotaEmailConfigurationVO.getAccountId());
+        sc.setParameters("email_template_id", 
quotaEmailConfigurationVO.getEmailTemplateId());
+        Transaction.execute(TransactionLegacy.USAGE_DB, new 
TransactionCallbackNoReturn() {
+            @Override
+            public void doInTransactionWithoutResult(TransactionStatus status) 
{
+                update(quotaEmailConfigurationVO, sc);
+            }
+        });
+
+        return quotaEmailConfigurationVO;
+    }
+
+    @Override
+    public void persistQuotaEmailConfiguration(QuotaEmailConfigurationVO 
quotaEmailConfigurationVO) {
+        Transaction.execute(TransactionLegacy.USAGE_DB, new 
TransactionCallbackNoReturn() {
+            @Override
+            public void doInTransactionWithoutResult(TransactionStatus status) 
{
+                persist(quotaEmailConfigurationVO);
+            }
+        });
+    }
+
+    @Override
+    public List<QuotaEmailConfigurationVO> listByAccount(long accountId) {
+        SearchCriteria<QuotaEmailConfigurationVO> sc = 
searchBuilderFindByIds.create();
+        sc.setParameters("account_id", accountId);
+
+        return Transaction.execute(TransactionLegacy.USAGE_DB, new 
TransactionCallback<List<QuotaEmailConfigurationVO>>() {
+            @Override
+            public List<QuotaEmailConfigurationVO> 
doInTransaction(TransactionStatus status) {
+                return listBy(sc);
+            }
+        });

Review Comment:
   IMO, we could increment readability here with lambda.



##########
plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaListEmailConfigurationCmd.java:
##########
@@ -0,0 +1,61 @@
+//Licensed to the Apache Software Foundation (ASF) under one
+//or more contributor license agreements.  See the NOTICE file
+//distributed with this work for additional information
+//regarding copyright ownership.  The ASF licenses this file
+//to you under the Apache License, Version 2.0 (the
+//"License"); you may not use this file except in compliance
+//with the License.  You may obtain a copy of the License at
+//
+//http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing,
+//software distributed under the License is distributed on an
+//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+//KIND, either express or implied.  See the License for the
+//specific language governing permissions and limitations
+//under the License.
+package org.apache.cloudstack.api.command;
+
+import com.cloud.user.Account;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.AccountResponse;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.QuotaConfigureEmailResponse;
+import org.apache.cloudstack.api.response.QuotaResponseBuilder;
+
+import javax.inject.Inject;
+
+@APICommand(name = QuotaListEmailConfigurationCmd.API_NAME, responseObject = 
QuotaConfigureEmailResponse.class, description = "List quota email template 
configurations", since = "4.19.0.0",
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
+public class QuotaListEmailConfigurationCmd extends BaseCmd {
+
+    public static final String API_NAME = "quotaListEmailConfiguration";

Review Comment:
   This is not the pattern anymore; the API name goes directly in the 
annotation.
   
   



##########
framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailConfigurationDaoImpl.java:
##########
@@ -0,0 +1,132 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.quota.dao;
+
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.JoinBuilder;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.TransactionCallback;
+import com.cloud.utils.db.TransactionCallbackNoReturn;
+import com.cloud.utils.db.TransactionLegacy;
+import com.cloud.utils.db.TransactionStatus;
+import org.apache.cloudstack.quota.constant.QuotaConfig;
+import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
+import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+import java.util.List;
+
+@Component
+public class QuotaEmailConfigurationDaoImpl extends 
GenericDaoBase<QuotaEmailConfigurationVO, Long> implements 
QuotaEmailConfigurationDao {
+
+    @Inject
+    private QuotaEmailTemplatesDao quotaEmailTemplatesDao;
+
+    private SearchBuilder<QuotaEmailConfigurationVO> searchBuilderFindByIds;
+
+    private SearchBuilder<QuotaEmailTemplatesVO> 
searchBuilderFindByTemplateName;
+
+    private SearchBuilder<QuotaEmailConfigurationVO> 
searchBuilderFindByTemplateTypeAndAccountId;
+
+    @PostConstruct
+    public void init() {
+        searchBuilderFindByIds = createSearchBuilder();
+        searchBuilderFindByIds.and("account_id", 
searchBuilderFindByIds.entity().getAccountId(), SearchCriteria.Op.EQ);
+        searchBuilderFindByIds.and("email_template_id", 
searchBuilderFindByIds.entity().getEmailTemplateId(), SearchCriteria.Op.EQ);
+        searchBuilderFindByIds.done();
+
+        searchBuilderFindByTemplateName = 
quotaEmailTemplatesDao.createSearchBuilder();
+        searchBuilderFindByTemplateName.and("template_name", 
searchBuilderFindByTemplateName.entity().getTemplateName(), 
SearchCriteria.Op.EQ);
+
+        searchBuilderFindByTemplateTypeAndAccountId = createSearchBuilder();
+        searchBuilderFindByTemplateTypeAndAccountId.and("account_id", 
searchBuilderFindByTemplateTypeAndAccountId.entity().getAccountId(), 
SearchCriteria.Op.EQ);
+        searchBuilderFindByTemplateTypeAndAccountId.join("email_template_id", 
searchBuilderFindByTemplateName, 
searchBuilderFindByTemplateName.entity().getId(),
+                
searchBuilderFindByTemplateTypeAndAccountId.entity().getEmailTemplateId(), 
JoinBuilder.JoinType.INNER);
+
+        searchBuilderFindByTemplateName.done();
+        searchBuilderFindByTemplateTypeAndAccountId.done();

Review Comment:
   If the logical operator are the same, we do not need to create several 
search builders; we could create just one and set the parameters on demand. ACS 
will create the queries based on the parameters set.



##########
framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailConfigurationDaoImpl.java:
##########
@@ -0,0 +1,132 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.quota.dao;
+
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.JoinBuilder;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.TransactionCallback;
+import com.cloud.utils.db.TransactionCallbackNoReturn;
+import com.cloud.utils.db.TransactionLegacy;
+import com.cloud.utils.db.TransactionStatus;
+import org.apache.cloudstack.quota.constant.QuotaConfig;
+import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
+import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+import java.util.List;
+
+@Component
+public class QuotaEmailConfigurationDaoImpl extends 
GenericDaoBase<QuotaEmailConfigurationVO, Long> implements 
QuotaEmailConfigurationDao {
+
+    @Inject
+    private QuotaEmailTemplatesDao quotaEmailTemplatesDao;
+
+    private SearchBuilder<QuotaEmailConfigurationVO> searchBuilderFindByIds;
+
+    private SearchBuilder<QuotaEmailTemplatesVO> 
searchBuilderFindByTemplateName;
+
+    private SearchBuilder<QuotaEmailConfigurationVO> 
searchBuilderFindByTemplateTypeAndAccountId;
+
+    @PostConstruct
+    public void init() {
+        searchBuilderFindByIds = createSearchBuilder();
+        searchBuilderFindByIds.and("account_id", 
searchBuilderFindByIds.entity().getAccountId(), SearchCriteria.Op.EQ);
+        searchBuilderFindByIds.and("email_template_id", 
searchBuilderFindByIds.entity().getEmailTemplateId(), SearchCriteria.Op.EQ);
+        searchBuilderFindByIds.done();
+
+        searchBuilderFindByTemplateName = 
quotaEmailTemplatesDao.createSearchBuilder();
+        searchBuilderFindByTemplateName.and("template_name", 
searchBuilderFindByTemplateName.entity().getTemplateName(), 
SearchCriteria.Op.EQ);
+
+        searchBuilderFindByTemplateTypeAndAccountId = createSearchBuilder();
+        searchBuilderFindByTemplateTypeAndAccountId.and("account_id", 
searchBuilderFindByTemplateTypeAndAccountId.entity().getAccountId(), 
SearchCriteria.Op.EQ);
+        searchBuilderFindByTemplateTypeAndAccountId.join("email_template_id", 
searchBuilderFindByTemplateName, 
searchBuilderFindByTemplateName.entity().getId(),
+                
searchBuilderFindByTemplateTypeAndAccountId.entity().getEmailTemplateId(), 
JoinBuilder.JoinType.INNER);
+
+        searchBuilderFindByTemplateName.done();
+        searchBuilderFindByTemplateTypeAndAccountId.done();
+    }
+
+    @Override
+    public QuotaEmailConfigurationVO findByAccountIdAndEmailTemplateId(long 
accountId, long emailTemplateId) {
+        SearchCriteria<QuotaEmailConfigurationVO> sc = 
searchBuilderFindByIds.create();
+        sc.setParameters("account_id", accountId);
+        sc.setParameters("email_template_id", emailTemplateId);
+        return Transaction.execute(TransactionLegacy.USAGE_DB, new 
TransactionCallback<QuotaEmailConfigurationVO>() {
+            @Override
+            public QuotaEmailConfigurationVO doInTransaction(TransactionStatus 
status) {
+                return findOneBy(sc);
+            }
+        });
+    }
+
+    @Override
+    public QuotaEmailConfigurationVO 
updateQuotaEmailConfiguration(QuotaEmailConfigurationVO 
quotaEmailConfigurationVO) {
+        SearchCriteria<QuotaEmailConfigurationVO> sc = 
searchBuilderFindByIds.create();
+        sc.setParameters("account_id", 
quotaEmailConfigurationVO.getAccountId());
+        sc.setParameters("email_template_id", 
quotaEmailConfigurationVO.getEmailTemplateId());
+        Transaction.execute(TransactionLegacy.USAGE_DB, new 
TransactionCallbackNoReturn() {
+            @Override
+            public void doInTransactionWithoutResult(TransactionStatus status) 
{
+                update(quotaEmailConfigurationVO, sc);
+            }
+        });

Review Comment:
   IMO, we could increment readability here with lambda.



##########
plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaConfigureEmailCmd.java:
##########
@@ -0,0 +1,86 @@
+//Licensed to the Apache Software Foundation (ASF) under one
+//or more contributor license agreements.  See the NOTICE file
+//distributed with this work for additional information
+//regarding copyright ownership.  The ASF licenses this file
+//to you under the Apache License, Version 2.0 (the
+//"License"); you may not use this file except in compliance
+//with the License.  You may obtain a copy of the License at
+//
+//http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing,
+//software distributed under the License is distributed on an
+//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+//KIND, either express or implied.  See the License for the
+//specific language governing permissions and limitations
+//under the License.
+package org.apache.cloudstack.api.command;
+
+import com.cloud.utils.Pair;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.AccountResponse;
+import org.apache.cloudstack.api.response.QuotaConfigureEmailResponse;
+import org.apache.cloudstack.api.response.QuotaResponseBuilder;
+import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
+
+import javax.inject.Inject;
+
+@APICommand(name = QuotaConfigureEmailCmd.API_NAME, responseObject = 
QuotaConfigureEmailResponse.class, description = "Configure a quota email 
template", since = "4.19.0.0",
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
+public class QuotaConfigureEmailCmd extends BaseCmd {
+
+    public static final String API_NAME = "quotaConfigureEmail";

Review Comment:
   This is not the pattern anymore; the API name goes directly in the 
annotation.



##########
framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDaoImpl.java:
##########
@@ -68,4 +68,14 @@ public Boolean doInTransaction(final TransactionStatus 
status) {
             }
         });
     }
+
+    @Override
+    public QuotaEmailTemplatesVO findById(long id) {
+        return Transaction.execute(TransactionLegacy.USAGE_DB, new 
TransactionCallback<QuotaEmailTemplatesVO>() {
+            @Override
+            public QuotaEmailTemplatesVO doInTransaction(final 
TransactionStatus status) {
+                return QuotaEmailTemplatesDaoImpl.super.findById(id);
+            }
+        });

Review Comment:
   IMO, we could increment readability here with lambda.



##########
plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaConfigureEmailCmd.java:
##########
@@ -0,0 +1,86 @@
+//Licensed to the Apache Software Foundation (ASF) under one
+//or more contributor license agreements.  See the NOTICE file
+//distributed with this work for additional information
+//regarding copyright ownership.  The ASF licenses this file
+//to you under the Apache License, Version 2.0 (the
+//"License"); you may not use this file except in compliance
+//with the License.  You may obtain a copy of the License at
+//
+//http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing,
+//software distributed under the License is distributed on an
+//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+//KIND, either express or implied.  See the License for the
+//specific language governing permissions and limitations
+//under the License.
+package org.apache.cloudstack.api.command;
+
+import com.cloud.utils.Pair;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.AccountResponse;
+import org.apache.cloudstack.api.response.QuotaConfigureEmailResponse;
+import org.apache.cloudstack.api.response.QuotaResponseBuilder;
+import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
+
+import javax.inject.Inject;
+
+@APICommand(name = QuotaConfigureEmailCmd.API_NAME, responseObject = 
QuotaConfigureEmailResponse.class, description = "Configure a quota email 
template", since = "4.19.0.0",

Review Comment:
   Change `since`.



##########
plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java:
##########
@@ -653,4 +658,93 @@ public boolean deleteQuotaTariff(String quotaTariffUuid) {
         quotaTariff.setRemoved(_quotaService.computeAdjustedTime(new Date()));
         return _quotaTariffDao.updateQuotaTariff(quotaTariff);
     }
+
+    @Override
+    public Pair<QuotaEmailConfigurationVO, Double> 
configureQuotaEmail(QuotaConfigureEmailCmd cmd) {
+        validateQuotaConfigureEmailCmdParameters(cmd);
+
+        Double minBalance = cmd.getMinBalance();
+
+        if (minBalance != null) {
+            _quotaService.setMinBalance(cmd.getAccountId(), 
cmd.getMinBalance());
+        }
+
+        if (cmd.getTemplateName() != null) {
+            List<QuotaEmailTemplatesVO> templateVO = 
_quotaEmailTemplateDao.listAllQuotaEmailTemplates(cmd.getTemplateName());
+            if (templateVO.isEmpty()) {
+                throw new InvalidParameterValueException(String.format("Could 
not find template with name [%s].", cmd.getTemplateName()));
+            }
+            long templateId = templateVO.get(0).getId();
+            QuotaEmailConfigurationVO configurationVO = 
quotaEmailConfigurationDao.findByAccountIdAndEmailTemplateId(cmd.getAccountId(),
 templateId);
+
+            if (configurationVO == null) {
+                configurationVO = new 
QuotaEmailConfigurationVO(cmd.getAccountId(), templateId, cmd.getEnable());
+                
quotaEmailConfigurationDao.persistQuotaEmailConfiguration(configurationVO);
+                return new Pair<>(configurationVO, minBalance);
+            }
+
+            configurationVO.setEnabled(cmd.getEnable());
+            return new 
Pair<>(quotaEmailConfigurationDao.updateQuotaEmailConfiguration(configurationVO),
 minBalance);
+        }

Review Comment:
   This block could be extracted to a method.



##########
plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaConfigureEmailCmd.java:
##########
@@ -0,0 +1,86 @@
+//Licensed to the Apache Software Foundation (ASF) under one
+//or more contributor license agreements.  See the NOTICE file
+//distributed with this work for additional information
+//regarding copyright ownership.  The ASF licenses this file
+//to you under the Apache License, Version 2.0 (the
+//"License"); you may not use this file except in compliance
+//with the License.  You may obtain a copy of the License at
+//
+//http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing,
+//software distributed under the License is distributed on an
+//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+//KIND, either express or implied.  See the License for the
+//specific language governing permissions and limitations
+//under the License.
+package org.apache.cloudstack.api.command;
+
+import com.cloud.utils.Pair;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.AccountResponse;
+import org.apache.cloudstack.api.response.QuotaConfigureEmailResponse;
+import org.apache.cloudstack.api.response.QuotaResponseBuilder;
+import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
+
+import javax.inject.Inject;
+
+@APICommand(name = QuotaConfigureEmailCmd.API_NAME, responseObject = 
QuotaConfigureEmailResponse.class, description = "Configure a quota email 
template", since = "4.19.0.0",
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
+public class QuotaConfigureEmailCmd extends BaseCmd {
+
+    public static final String API_NAME = "quotaConfigureEmail";
+
+    @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, 
entityType = AccountResponse.class, required = true,
+            description = "Account ID for which to configure quota template 
email or min balance")
+    private long accountId;
+
+    @Parameter(name = ApiConstants.TEMPLATE_NAME, type = CommandType.STRING, 
description = "Quota email template name which should be configured")
+    private String templateName;
+
+    @Parameter(name = ApiConstants.ENABLE, type = CommandType.BOOLEAN, 
description = "If the quota email template should be enabled")
+    private Boolean enable;
+
+    @Parameter(name = "minbalance", type = CommandType.DOUBLE, description = 
"New quota account min balance")
+    private Double minBalance;
+
+    @Inject
+    private QuotaResponseBuilder responseBuilder;
+
+    @Override
+    public void execute() {
+        Pair<QuotaEmailConfigurationVO, Double> result = 
responseBuilder.configureQuotaEmail(this);
+        QuotaConfigureEmailResponse quotaConfigureEmailResponse = 
responseBuilder.createQuotaConfigureEmailResponse(result.first(), 
result.second(), accountId);
+        quotaConfigureEmailResponse.setResponseName(getCommandName());
+        this.setResponseObject(quotaConfigureEmailResponse);
+    }
+
+    @Override
+    public String getCommandName() {
+        return API_NAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }

Review Comment:
   Unnecessary.



##########
plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaListEmailConfigurationCmd.java:
##########
@@ -0,0 +1,61 @@
+//Licensed to the Apache Software Foundation (ASF) under one
+//or more contributor license agreements.  See the NOTICE file
+//distributed with this work for additional information
+//regarding copyright ownership.  The ASF licenses this file
+//to you under the Apache License, Version 2.0 (the
+//"License"); you may not use this file except in compliance
+//with the License.  You may obtain a copy of the License at
+//
+//http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing,
+//software distributed under the License is distributed on an
+//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+//KIND, either express or implied.  See the License for the
+//specific language governing permissions and limitations
+//under the License.
+package org.apache.cloudstack.api.command;
+
+import com.cloud.user.Account;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.AccountResponse;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.QuotaConfigureEmailResponse;
+import org.apache.cloudstack.api.response.QuotaResponseBuilder;
+
+import javax.inject.Inject;
+
+@APICommand(name = QuotaListEmailConfigurationCmd.API_NAME, responseObject = 
QuotaConfigureEmailResponse.class, description = "List quota email template 
configurations", since = "4.19.0.0",
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
+public class QuotaListEmailConfigurationCmd extends BaseCmd {
+
+    public static final String API_NAME = "quotaListEmailConfiguration";
+
+    @Parameter(name = ApiConstants.ACCOUNT_ID, type = 
BaseCmd.CommandType.UUID, entityType = AccountResponse.class, required = true,
+            description = "Account ID for which to list quota template email 
configurations")
+    private long accountId;
+
+    @Inject
+    private QuotaResponseBuilder responseBuilder;
+
+    @Override
+    public void execute() {
+        ListResponse<QuotaConfigureEmailResponse> response = new 
ListResponse<>();
+        
response.setResponses(responseBuilder.listEmailConfiguration(accountId));
+        response.setResponseName(getCommandName());
+        setResponseObject(response);
+    }
+
+    @Override
+    public String getCommandName() {
+        return API_NAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
+    }

Review Comment:
   Unnecessary.



##########
framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailConfigurationDaoImpl.java:
##########
@@ -0,0 +1,132 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.quota.dao;
+
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.JoinBuilder;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.db.TransactionCallback;
+import com.cloud.utils.db.TransactionCallbackNoReturn;
+import com.cloud.utils.db.TransactionLegacy;
+import com.cloud.utils.db.TransactionStatus;
+import org.apache.cloudstack.quota.constant.QuotaConfig;
+import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
+import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+import java.util.List;
+
+@Component
+public class QuotaEmailConfigurationDaoImpl extends 
GenericDaoBase<QuotaEmailConfigurationVO, Long> implements 
QuotaEmailConfigurationDao {
+
+    @Inject
+    private QuotaEmailTemplatesDao quotaEmailTemplatesDao;
+
+    private SearchBuilder<QuotaEmailConfigurationVO> searchBuilderFindByIds;
+
+    private SearchBuilder<QuotaEmailTemplatesVO> 
searchBuilderFindByTemplateName;
+
+    private SearchBuilder<QuotaEmailConfigurationVO> 
searchBuilderFindByTemplateTypeAndAccountId;
+
+    @PostConstruct
+    public void init() {
+        searchBuilderFindByIds = createSearchBuilder();
+        searchBuilderFindByIds.and("account_id", 
searchBuilderFindByIds.entity().getAccountId(), SearchCriteria.Op.EQ);
+        searchBuilderFindByIds.and("email_template_id", 
searchBuilderFindByIds.entity().getEmailTemplateId(), SearchCriteria.Op.EQ);
+        searchBuilderFindByIds.done();
+
+        searchBuilderFindByTemplateName = 
quotaEmailTemplatesDao.createSearchBuilder();
+        searchBuilderFindByTemplateName.and("template_name", 
searchBuilderFindByTemplateName.entity().getTemplateName(), 
SearchCriteria.Op.EQ);
+
+        searchBuilderFindByTemplateTypeAndAccountId = createSearchBuilder();
+        searchBuilderFindByTemplateTypeAndAccountId.and("account_id", 
searchBuilderFindByTemplateTypeAndAccountId.entity().getAccountId(), 
SearchCriteria.Op.EQ);
+        searchBuilderFindByTemplateTypeAndAccountId.join("email_template_id", 
searchBuilderFindByTemplateName, 
searchBuilderFindByTemplateName.entity().getId(),
+                
searchBuilderFindByTemplateTypeAndAccountId.entity().getEmailTemplateId(), 
JoinBuilder.JoinType.INNER);
+
+        searchBuilderFindByTemplateName.done();
+        searchBuilderFindByTemplateTypeAndAccountId.done();
+    }
+
+    @Override
+    public QuotaEmailConfigurationVO findByAccountIdAndEmailTemplateId(long 
accountId, long emailTemplateId) {
+        SearchCriteria<QuotaEmailConfigurationVO> sc = 
searchBuilderFindByIds.create();
+        sc.setParameters("account_id", accountId);
+        sc.setParameters("email_template_id", emailTemplateId);
+        return Transaction.execute(TransactionLegacy.USAGE_DB, new 
TransactionCallback<QuotaEmailConfigurationVO>() {
+            @Override
+            public QuotaEmailConfigurationVO doInTransaction(TransactionStatus 
status) {
+                return findOneBy(sc);
+            }
+        });
+    }
+
+    @Override
+    public QuotaEmailConfigurationVO 
updateQuotaEmailConfiguration(QuotaEmailConfigurationVO 
quotaEmailConfigurationVO) {
+        SearchCriteria<QuotaEmailConfigurationVO> sc = 
searchBuilderFindByIds.create();
+        sc.setParameters("account_id", 
quotaEmailConfigurationVO.getAccountId());
+        sc.setParameters("email_template_id", 
quotaEmailConfigurationVO.getEmailTemplateId());
+        Transaction.execute(TransactionLegacy.USAGE_DB, new 
TransactionCallbackNoReturn() {
+            @Override
+            public void doInTransactionWithoutResult(TransactionStatus status) 
{
+                update(quotaEmailConfigurationVO, sc);
+            }
+        });
+
+        return quotaEmailConfigurationVO;
+    }
+
+    @Override
+    public void persistQuotaEmailConfiguration(QuotaEmailConfigurationVO 
quotaEmailConfigurationVO) {
+        Transaction.execute(TransactionLegacy.USAGE_DB, new 
TransactionCallbackNoReturn() {
+            @Override
+            public void doInTransactionWithoutResult(TransactionStatus status) 
{
+                persist(quotaEmailConfigurationVO);
+            }
+        });

Review Comment:
   IMO, we could increment readability here with lambda.



##########
plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaListEmailConfigurationCmd.java:
##########
@@ -0,0 +1,61 @@
+//Licensed to the Apache Software Foundation (ASF) under one
+//or more contributor license agreements.  See the NOTICE file
+//distributed with this work for additional information
+//regarding copyright ownership.  The ASF licenses this file
+//to you under the Apache License, Version 2.0 (the
+//"License"); you may not use this file except in compliance
+//with the License.  You may obtain a copy of the License at
+//
+//http://www.apache.org/licenses/LICENSE-2.0
+//
+//Unless required by applicable law or agreed to in writing,
+//software distributed under the License is distributed on an
+//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+//KIND, either express or implied.  See the License for the
+//specific language governing permissions and limitations
+//under the License.
+package org.apache.cloudstack.api.command;
+
+import com.cloud.user.Account;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.AccountResponse;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.QuotaConfigureEmailResponse;
+import org.apache.cloudstack.api.response.QuotaResponseBuilder;
+
+import javax.inject.Inject;
+
+@APICommand(name = QuotaListEmailConfigurationCmd.API_NAME, responseObject = 
QuotaConfigureEmailResponse.class, description = "List quota email template 
configurations", since = "4.19.0.0",

Review Comment:
   Change since.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to