This is an automated email from the ASF dual-hosted git repository.
joao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/main by this push:
new f76c6f3ea97 Quota email configuration (#8307)
f76c6f3ea97 is described below
commit f76c6f3ea9710b70770c26193eaccd1caa6207e0
Author: João Jandre <[email protected]>
AuthorDate: Mon Mar 18 08:26:41 2024 -0300
Quota email configuration (#8307)
* Quota email configuration feature
---
.../resources/META-INF/db/schema-41900to42000.sql | 10 ++
.../apache/cloudstack/quota/QuotaAlertManager.java | 3 +
.../cloudstack/quota/QuotaAlertManagerImpl.java | 130 +++++++++++-----
.../cloudstack/quota/QuotaStatementImpl.java | 43 ++++--
.../cloudstack/quota/constant/QuotaConfig.java | 3 +
.../quota/dao/QuotaEmailConfigurationDao.java | 36 +++++
.../quota/dao/QuotaEmailConfigurationDaoImpl.java | 105 +++++++++++++
.../quota/dao/QuotaEmailTemplatesDao.java | 2 +
.../quota/dao/QuotaEmailTemplatesDaoImpl.java | 5 +
.../quota/vo/QuotaEmailConfigurationVO.java | 68 +++++++++
.../quota/spring-framework-quota-context.xml | 1 +
.../quota/QuotaAlertManagerImplTest.java | 163 +++++++++++++++++----
.../cloudstack/quota/QuotaStatementTest.java | 67 ++++++++-
.../api/command/QuotaConfigureEmailCmd.java | 79 ++++++++++
.../command/QuotaListEmailConfigurationCmd.java | 54 +++++++
.../api/response/QuotaConfigureEmailResponse.java | 78 ++++++++++
.../api/response/QuotaResponseBuilder.java | 8 +
.../api/response/QuotaResponseBuilderImpl.java | 106 +++++++++++++-
.../apache/cloudstack/quota/QuotaServiceImpl.java | 6 +-
.../api/response/QuotaResponseBuilderImplTest.java | 112 ++++++++++++++
20 files changed, 988 insertions(+), 91 deletions(-)
diff --git
a/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql
b/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql
index 3ebab6b15f2..1bb1905443a 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql
@@ -69,3 +69,13 @@ CALL
`cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.network_offerings','for_nsx', 'int(1
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.network_offerings','nsx_mode',
'varchar(32) COMMENT "mode in which the network would route traffic"');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','for_nsx', 'int(1)
unsigned DEFAULT "0" COMMENT "is nsx enabled for the resource"');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','nsx_mode',
'varchar(32) COMMENT "mode in which the network would route traffic"');
+
+
+-- Create table to persist quota email template configurations
+CREATE TABLE IF NOT EXISTS `cloud_usage`.`quota_email_configuration`(
+ `account_id` int(11) NOT NULL,
+ `email_template_id` bigint(20) NOT NULL,
+ `enabled` int(1) UNSIGNED NOT NULL,
+ PRIMARY KEY (`account_id`, `email_template_id`),
+ CONSTRAINT `FK_quota_email_configuration_account_id` FOREIGN KEY
(`account_id`) REFERENCES `cloud_usage`.`quota_account`(`account_id`),
+ CONSTRAINT `FK_quota_email_configuration_email_template_id` FOREIGN KEY
(`email_template_id`) REFERENCES `cloud_usage`.`quota_email_templates`(`id`));
diff --git
a/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaAlertManager.java
b/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaAlertManager.java
index 44204e8d116..f4ee2362c7e 100644
---
a/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaAlertManager.java
+++
b/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaAlertManager.java
@@ -16,11 +16,14 @@
//under the License.
package org.apache.cloudstack.quota;
+import com.cloud.user.AccountVO;
import com.cloud.utils.component.Manager;
import org.apache.cloudstack.quota.QuotaAlertManagerImpl.DeferredQuotaEmail;
+import org.apache.cloudstack.quota.constant.QuotaConfig;
public interface QuotaAlertManager extends Manager {
+ boolean isQuotaEmailTypeEnabledForAccount(AccountVO account,
QuotaConfig.QuotaEmailTemplateTypes quotaEmailTemplateType);
void checkAndSendQuotaAlertEmails();
void sendQuotaAlert(DeferredQuotaEmail emailToBeSent);
}
diff --git
a/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java
b/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java
index ff41a8141d7..b26b3171f5b 100644
---
a/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java
+++
b/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java
@@ -34,8 +34,10 @@ import
org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.quota.constant.QuotaConfig;
import
org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
import org.apache.cloudstack.quota.dao.QuotaAccountDao;
+import org.apache.cloudstack.quota.dao.QuotaEmailConfigurationDao;
import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
import org.apache.cloudstack.quota.vo.QuotaAccountVO;
+import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.text.StrSubstitutor;
@@ -80,7 +82,10 @@ public class QuotaAlertManagerImpl extends ManagerBase
implements QuotaAlertMana
@Inject
private QuotaManager _quotaManager;
- private boolean _lockAccountEnforcement = false;
+ @Inject
+ private QuotaEmailConfigurationDao quotaEmailConfigurationDao;
+
+ protected boolean _lockAccountEnforcement = false;
private String senderAddress;
protected SMTPMailSender mailSender;
@@ -139,55 +144,100 @@ public class QuotaAlertManagerImpl extends ManagerBase
implements QuotaAlertMana
return true;
}
+ /**
+ * Returns whether a Quota email type is enabled or not for the provided
account.
+ */
+ @Override
+ public boolean isQuotaEmailTypeEnabledForAccount(AccountVO account,
QuotaEmailTemplateTypes quotaEmailTemplateType) {
+ boolean quotaEmailsEnabled =
QuotaConfig.QuotaEnableEmails.valueIn(account.getAccountId());
+ if (!quotaEmailsEnabled) {
+ logger.debug("Configuration [{}] is disabled for account [{}].
Therefore, the account will not receive Quota email of type [{}].",
QuotaConfig.QuotaEnableEmails.key(), account, quotaEmailTemplateType);
+ return false;
+ }
+
+ QuotaEmailConfigurationVO quotaEmail =
quotaEmailConfigurationDao.findByAccountIdAndEmailTemplateType(account.getAccountId(),
quotaEmailTemplateType);
+
+ boolean emailEnabled = quotaEmail == null || quotaEmail.isEnabled();
+ if (emailEnabled) {
+ logger.debug("Quota email [{}] is enabled for account [{}].",
quotaEmailTemplateType, account);
+ } else {
+ logger.debug("Quota email [{}] has been manually disabled for
account [{}] through the API quotaConfigureEmail.", quotaEmailTemplateType,
account);
+ }
+ return emailEnabled;
+ }
+
+
@Override
public void checkAndSendQuotaAlertEmails() {
List<DeferredQuotaEmail> deferredQuotaEmailList = new
ArrayList<DeferredQuotaEmail>();
- final BigDecimal zeroBalance = new BigDecimal(0);
+
+ logger.info("Checking and sending quota alert emails.");
for (final QuotaAccountVO quotaAccount :
_quotaAcc.listAllQuotaAccount()) {
- if (logger.isDebugEnabled()) {
- 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
- }
- logger.debug("checkAndSendQuotaAlertEmails: Check id={}
bal={}, alertDate={}, lockable={}", account.getId(),
- accountBalance,
DateUtil.displayDateInTimezone(QuotaManagerImpl.getUsageAggregationTimeZone(),
alertDate),
- lockable);
- if (accountBalance.compareTo(zeroBalance) < 0) {
- if (_lockAccountEnforcement && (lockable == 1)) {
- if (_quotaManager.isLockable(account)) {
- logger.info("Locking account " +
account.getAccountName() + " due to quota < 0.");
- lockAccount(account.getId());
- }
- }
- if (alertDate == null || (balanceDate.after(alertDate) &&
getDifferenceDays(alertDate, new Date()) > 1)) {
- 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)) {
- 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 (logger.isDebugEnabled()) {
- logger.debug("checkAndSendQuotaAlertEmails: Attempting to send
quota alert email to users of account: " +
emailToBeSent.getAccount().getAccountName());
- }
+ logger.debug("Attempting to send a quota alert email to users of
account [{}].", 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) {
+ logger.debug("Checking {} for email alerts.", quotaAccount);
+ BigDecimal accountBalance = quotaAccount.getQuotaBalance();
+
+ if (accountBalance == null) {
+ logger.debug("{} has a null balance, therefore it will not receive
quota alert emails.", quotaAccount);
+ return;
+ }
+
+ AccountVO account = _accountDao.findById(quotaAccount.getId());
+ if (account == null) {
+ logger.debug("Account of {} is removed, thus it will not receive
quota alert emails.", quotaAccount);
+ return;
+ }
+
+ checkBalanceAndAddToEmailList(deferredQuotaEmailList, quotaAccount,
account, accountBalance);
+ }
+
+ private void checkBalanceAndAddToEmailList(List<DeferredQuotaEmail>
deferredQuotaEmailList, QuotaAccountVO quotaAccount, AccountVO account,
BigDecimal accountBalance) {
+ Date balanceDate = quotaAccount.getQuotaBalanceDate();
+ Date alertDate = quotaAccount.getQuotaAlertDate();
+ int lockable = quotaAccount.getQuotaEnforce();
+ BigDecimal thresholdBalance = quotaAccount.getQuotaMinBalance();
+
+ logger.debug("Checking {} with accountBalance [{}], alertDate [{}] and
lockable [{}] to see if a quota alert email should be sent.", account,
+ accountBalance,
DateUtil.displayDateInTimezone(QuotaManagerImpl.getUsageAggregationTimeZone(),
alertDate), lockable);
+
+ boolean shouldSendEmail = alertDate == null ||
(balanceDate.after(alertDate) && getDifferenceDays(alertDate, new Date()) > 1);
+
+ if (accountBalance.compareTo(BigDecimal.ZERO) < 0) {
+ if (_lockAccountEnforcement && lockable == 1 &&
_quotaManager.isLockable(account)) {
+ logger.info("Locking {}, as quota balance is lower than 0.",
account);
+ lockAccount(account.getId());
+ }
+
+ boolean quotaEmptyEmailEnabled =
isQuotaEmailTypeEnabledForAccount(account, QuotaEmailTemplateTypes.QUOTA_EMPTY);
+ if (quotaEmptyEmailEnabled && shouldSendEmail) {
+ logger.debug("Adding {} to the deferred emails list, as quota
balance is lower than 0.", account);
+ deferredQuotaEmailList.add(new DeferredQuotaEmail(account,
quotaAccount, QuotaEmailTemplateTypes.QUOTA_EMPTY));
+ return;
+ }
+ } else if (accountBalance.compareTo(thresholdBalance) < 0) {
+ boolean quotaLowEmailEnabled =
isQuotaEmailTypeEnabledForAccount(account, QuotaEmailTemplateTypes.QUOTA_LOW);
+ if (quotaLowEmailEnabled && shouldSendEmail) {
+ logger.debug("Adding {} to the deferred emails list, as quota
balance [{}] is below the threshold [{}].", account, accountBalance,
thresholdBalance);
+ deferredQuotaEmailList.add(new DeferredQuotaEmail(account,
quotaAccount, QuotaEmailTemplateTypes.QUOTA_LOW));
+ return;
+ }
+ }
+ logger.debug("{} will not receive any quota alert emails in this
round.", account);
+ }
+
@Override
public void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
final AccountVO account = emailToBeSent.getAccount();
@@ -285,7 +335,7 @@ public class QuotaAlertManagerImpl extends ManagerBase
implements QuotaAlertMana
return optionMap;
}
- public static long getDifferenceDays(Date d1, Date d2) {
+ public long getDifferenceDays(Date d1, Date d2) {
long diff = d2.getTime() - d1.getTime();
return TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
}
diff --git
a/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaStatementImpl.java
b/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaStatementImpl.java
index a4ee0e2584e..5ee327fb9a5 100644
---
a/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaStatementImpl.java
+++
b/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaStatementImpl.java
@@ -31,6 +31,8 @@ import
org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.quota.QuotaAlertManagerImpl.DeferredQuotaEmail;
import org.apache.cloudstack.quota.constant.QuotaConfig;
import org.apache.cloudstack.quota.dao.QuotaAccountDao;
+import org.apache.cloudstack.quota.dao.QuotaEmailConfigurationDao;
+import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
import org.apache.cloudstack.quota.dao.QuotaUsageDao;
import org.apache.cloudstack.quota.vo.QuotaAccountVO;
import org.springframework.stereotype.Component;
@@ -53,6 +55,12 @@ public class QuotaStatementImpl extends ManagerBase
implements QuotaStatement {
@Inject
private ConfigurationDao _configDao;
+ @Inject
+ private QuotaEmailConfigurationDao quotaEmailConfigurationDao;
+
+ @Inject
+ private QuotaEmailTemplatesDao quotaEmailTemplatesDao;
+
final public static int s_LAST_STATEMENT_SENT_DAYS = 6; //ideally should
be less than 7 days
public enum QuotaStatementPeriods {
@@ -111,29 +119,34 @@ public class QuotaStatementImpl extends ManagerBase
implements QuotaStatement {
if (quotaAccount.getQuotaBalance() == null) {
continue; // no quota usage for this account ever, ignore
}
+ AccountVO account = _accountDao.findById(quotaAccount.getId());
+ if (account == null) {
+ logger.debug("Could not find an account corresponding to [{}].
Therefore, the statement email will not be sent.", quotaAccount);
+ continue;
+ }
+
+ boolean quotaStatementEmailEnabled =
_quotaAlert.isQuotaEmailTypeEnabledForAccount(account,
QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT);
+ if (!quotaStatementEmailEnabled) {
+ logger.debug("{} has [{}] email disabled. Therefore the email
will not be sent.", quotaAccount,
QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT);
+ continue;
+ }
//check if it is statement time
Calendar interval[] = statementTime(Calendar.getInstance(),
_period);
Date lastStatementDate = quotaAccount.getLastStatementDate();
if (interval != null) {
- AccountVO account = _accountDao.findById(quotaAccount.getId());
- if (account != null) {
- if (lastStatementDate == null ||
getDifferenceDays(lastStatementDate, new Date()) >= s_LAST_STATEMENT_SENT_DAYS
+ 1) {
- BigDecimal quotaUsage =
_quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(),
null, interval[0].getTime(), interval[1].getTime());
- logger.info("For account=" + quotaAccount.getId() + ",
quota used = " + quotaUsage);
- // send statement
- deferredQuotaEmailList.add(new
DeferredQuotaEmail(account, quotaAccount, quotaUsage,
QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
- } else {
- if (logger.isDebugEnabled()) {
- logger.debug("For " + quotaAccount.getId() + " the
statement has been sent recently");
-
- }
- }
+ if (lastStatementDate == null ||
getDifferenceDays(lastStatementDate, new Date()) >= s_LAST_STATEMENT_SENT_DAYS
+ 1) {
+ BigDecimal quotaUsage =
_quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(),
null, interval[0].getTime(), interval[1].getTime());
+ logger.info("Quota statement for account [{}] has an usage
of [{}].", quotaAccount, quotaUsage);
+
+ // send statement
+ deferredQuotaEmailList.add(new DeferredQuotaEmail(account,
quotaAccount, quotaUsage, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_STATEMENT));
+ } else {
+ logger.debug("Quota statement has already been sent
recently to account [{}].", quotaAccount);
}
} else if (lastStatementDate != null) {
- logger.info("For " + quotaAccount.getId() + " it is already
more than " + getDifferenceDays(lastStatementDate, new Date())
- + " days, will send statement in next cycle");
+ logger.info("For account {} it is already more than {} days,
will send statement in next cycle.", quotaAccount.getId(),
getDifferenceDays(lastStatementDate, new Date()));
}
}
diff --git
a/framework/quota/src/main/java/org/apache/cloudstack/quota/constant/QuotaConfig.java
b/framework/quota/src/main/java/org/apache/cloudstack/quota/constant/QuotaConfig.java
index 59aa54424cf..df7ffa5c3cd 100644
---
a/framework/quota/src/main/java/org/apache/cloudstack/quota/constant/QuotaConfig.java
+++
b/framework/quota/src/main/java/org/apache/cloudstack/quota/constant/QuotaConfig.java
@@ -72,6 +72,9 @@ public interface QuotaConfig {
ConfigKey<String> QuotaEmailFooter = new ConfigKey<>("Advanced",
String.class, "quota.email.footer", "",
"Text to be added as a footer for quota emails. Line breaks are
not automatically inserted between this section and the body.", true,
ConfigKey.Scope.Domain);
+ ConfigKey<Boolean> QuotaEnableEmails = new ConfigKey<>("Advanced",
Boolean.class, "quota.enable.emails", "true",
+ "Indicates whether Quota emails should be sent or not to accounts.
When enabled, the behavior for each account can be overridden through the API
quotaConfigureEmail.", true, ConfigKey.Scope.Account);
+
enum QuotaEmailTemplateTypes {
QUOTA_LOW, QUOTA_EMPTY, QUOTA_UNLOCK_ACCOUNT, QUOTA_STATEMENT
}
diff --git
a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailConfigurationDao.java
b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailConfigurationDao.java
new file mode 100644
index 00000000000..4bb3395cc11
--- /dev/null
+++
b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailConfigurationDao.java
@@ -0,0 +1,36 @@
+// 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.GenericDao;
+import org.apache.cloudstack.quota.constant.QuotaConfig;
+import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
+
+import java.util.List;
+
+public interface QuotaEmailConfigurationDao extends
GenericDao<QuotaEmailConfigurationVO, Long> {
+
+ QuotaEmailConfigurationVO findByAccountIdAndEmailTemplateId(long
accountId, long emailTemplateId);
+
+ QuotaEmailConfigurationVO
updateQuotaEmailConfiguration(QuotaEmailConfigurationVO
quotaEmailConfigurationVO);
+
+ void persistQuotaEmailConfiguration(QuotaEmailConfigurationVO
quotaEmailConfigurationVO);
+
+ List<QuotaEmailConfigurationVO> listByAccount(long accountId);
+
+ QuotaEmailConfigurationVO findByAccountIdAndEmailTemplateType(long
accountId, QuotaConfig.QuotaEmailTemplateTypes quotaEmailTemplateType);
+}
diff --git
a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailConfigurationDaoImpl.java
b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailConfigurationDaoImpl.java
new file mode 100644
index 00000000000..9466340ad05
--- /dev/null
+++
b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailConfigurationDaoImpl.java
@@ -0,0 +1,105 @@
+// 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.TransactionLegacy;
+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,
(TransactionCallback<QuotaEmailConfigurationVO>) status -> 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,
(TransactionCallback<Integer>) status -> update(quotaEmailConfigurationVO, sc));
+
+ return quotaEmailConfigurationVO;
+ }
+
+ @Override
+ public void persistQuotaEmailConfiguration(QuotaEmailConfigurationVO
quotaEmailConfigurationVO) {
+ Transaction.execute(TransactionLegacy.USAGE_DB,
(TransactionCallback<QuotaEmailConfigurationVO>) 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,
(TransactionCallback<List<QuotaEmailConfigurationVO>>) status -> 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,
(TransactionCallback<QuotaEmailConfigurationVO>) status -> findOneBy(sc));
+ }
+}
diff --git
a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDao.java
b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDao.java
index 573a7539744..346bb9a4a6a 100644
---
a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDao.java
+++
b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDao.java
@@ -24,4 +24,6 @@ import java.util.List;
public interface QuotaEmailTemplatesDao extends
GenericDao<QuotaEmailTemplatesVO, Long> {
List<QuotaEmailTemplatesVO> listAllQuotaEmailTemplates(String
templateName);
boolean updateQuotaEmailTemplate(QuotaEmailTemplatesVO template);
+
+ QuotaEmailTemplatesVO findById(long id);
}
diff --git
a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDaoImpl.java
b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDaoImpl.java
index b44ace0a1ff..c27f2df299b 100644
---
a/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDaoImpl.java
+++
b/framework/quota/src/main/java/org/apache/cloudstack/quota/dao/QuotaEmailTemplatesDaoImpl.java
@@ -66,4 +66,9 @@ public class QuotaEmailTemplatesDaoImpl extends
GenericDaoBase<QuotaEmailTemplat
}
});
}
+
+ @Override
+ public QuotaEmailTemplatesVO findById(long id) {
+ return Transaction.execute(TransactionLegacy.USAGE_DB,
(TransactionCallback<QuotaEmailTemplatesVO>) status ->
QuotaEmailTemplatesDaoImpl.super.findById(id));
+ }
}
diff --git
a/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaEmailConfigurationVO.java
b/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaEmailConfigurationVO.java
new file mode 100644
index 00000000000..e50c7ce6250
--- /dev/null
+++
b/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaEmailConfigurationVO.java
@@ -0,0 +1,68 @@
+// 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.vo;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "quota_email_configuration")
+public class QuotaEmailConfigurationVO {
+
+ @Column(name = "account_id")
+ private long accountId;
+
+ @Column(name = "email_template_id")
+ private long emailTemplateId;
+
+ @Column(name = "enabled")
+ private boolean enabled;
+
+ public QuotaEmailConfigurationVO() {
+ }
+
+ public QuotaEmailConfigurationVO(long accountId, long emailTemplateId,
boolean enable) {
+ this.accountId = accountId;
+ this.emailTemplateId = emailTemplateId;
+ this.enabled = enable;
+ }
+
+ public long getAccountId() {
+ return accountId;
+ }
+
+ public void setAccountId(long accountId) {
+ this.accountId = accountId;
+ }
+
+ public long getEmailTemplateId() {
+ return emailTemplateId;
+ }
+
+ public void setEmailTemplateId(long emailTemplateId) {
+ this.emailTemplateId = emailTemplateId;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+}
diff --git
a/framework/quota/src/main/resources/META-INF/cloudstack/quota/spring-framework-quota-context.xml
b/framework/quota/src/main/resources/META-INF/cloudstack/quota/spring-framework-quota-context.xml
index 5f1c274f049..e634321208f 100644
---
a/framework/quota/src/main/resources/META-INF/cloudstack/quota/spring-framework-quota-context.xml
+++
b/framework/quota/src/main/resources/META-INF/cloudstack/quota/spring-framework-quota-context.xml
@@ -30,5 +30,6 @@
<bean id="QuotaManager"
class="org.apache.cloudstack.quota.QuotaManagerImpl" />
<bean id="QuotaAlertManager"
class="org.apache.cloudstack.quota.QuotaAlertManagerImpl" />
<bean id="QuotaStatement"
class="org.apache.cloudstack.quota.QuotaStatementImpl" />
+ <bean id="QuotaEmailConfigurationDao"
class="org.apache.cloudstack.quota.dao.QuotaEmailConfigurationDaoImpl"/>
</beans>
diff --git
a/framework/quota/src/test/java/org/apache/cloudstack/quota/QuotaAlertManagerImplTest.java
b/framework/quota/src/test/java/org/apache/cloudstack/quota/QuotaAlertManagerImplTest.java
index a0dd4c22814..54d4f1d5b69 100644
---
a/framework/quota/src/test/java/org/apache/cloudstack/quota/QuotaAlertManagerImplTest.java
+++
b/framework/quota/src/test/java/org/apache/cloudstack/quota/QuotaAlertManagerImplTest.java
@@ -30,9 +30,12 @@ import javax.naming.ConfigurationException;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.quota.constant.QuotaConfig;
import org.apache.cloudstack.quota.dao.QuotaAccountDao;
+import org.apache.cloudstack.quota.dao.QuotaEmailConfigurationDaoImpl;
import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
import org.apache.cloudstack.quota.vo.QuotaAccountVO;
+import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -71,6 +74,9 @@ public class QuotaAlertManagerImplTest extends TestCase {
@Mock
private ConfigurationDao configDao;
+ @Mock
+ private QuotaEmailConfigurationDaoImpl quotaEmailConfigurationDaoMock;
+
@Mock
private QuotaAccountVO quotaAccountVOMock;
@@ -92,43 +98,142 @@ public class QuotaAlertManagerImplTest extends TestCase {
@Before
public void setup() throws IllegalAccessException, NoSuchFieldException,
ConfigurationException {
- TransactionLegacy.open("QuotaAlertManagerImplTest");
- }
-
- @Test
- public void testCheckAndSendQuotaAlertEmails() {
AccountVO accountVO = new AccountVO();
accountVO.setId(2L);
accountVO.setDomainId(1L);
accountVO.setType(Account.Type.NORMAL);
Mockito.when(accountDao.findById(Mockito.anyLong())).thenReturn(accountVO);
- QuotaAccountVO acc = new QuotaAccountVO(2L);
- acc.setQuotaBalance(new BigDecimal(404));
- acc.setQuotaMinBalance(new BigDecimal(100));
- acc.setQuotaBalanceDate(new Date());
- acc.setQuotaAlertDate(null);
- acc.setQuotaEnforce(0);
- List<QuotaAccountVO> accounts = new ArrayList<>();
- accounts.add(acc);
- Mockito.when(quotaAcc.listAllQuotaAccount()).thenReturn(accounts);
+ Mockito.doReturn(new
BigDecimal(404)).when(quotaAccountVOMock).getQuotaBalance();
+ Mockito.doReturn(new
BigDecimal(100)).when(quotaAccountVOMock).getQuotaMinBalance();
+
Mockito.doReturn(balanceDateMock).when(quotaAccountVOMock).getQuotaBalanceDate();
+ Mockito.doReturn(null).when(quotaAccountVOMock).getQuotaAlertDate();
+ Mockito.doReturn(0).when(quotaAccountVOMock).getQuotaEnforce();
- // Don't test sendQuotaAlert yet
-
Mockito.doNothing().when(quotaAlertManager).sendQuotaAlert(Mockito.any(QuotaAlertManagerImpl.DeferredQuotaEmail.class));
-
Mockito.lenient().doReturn(true).when(quotaAlertManager).lockAccount(Mockito.anyLong());
+ TransactionLegacy.open("QuotaAlertManagerImplTest");
+ }
+
+ @Test
+ public void
isQuotaEmailTypeEnabledForAccountTestConfigurationIsEnabledAndEmailIsConfiguredReturnConfiguredValue()
{
+ boolean expectedValue = !QuotaConfig.QuotaEnableEmails.value();
+ QuotaEmailConfigurationVO quotaEmailConfigurationVoMock =
Mockito.mock(QuotaEmailConfigurationVO.class);
+
Mockito.when(quotaEmailConfigurationVoMock.isEnabled()).thenReturn(expectedValue);
+
Mockito.doReturn(quotaEmailConfigurationVoMock).when(quotaEmailConfigurationDaoMock).findByAccountIdAndEmailTemplateType(Mockito.anyLong(),
Mockito.any(QuotaConfig.QuotaEmailTemplateTypes.class));
+
+ boolean result =
quotaAlertManager.isQuotaEmailTypeEnabledForAccount(accountMock,
QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY);
+
+ Assert.assertEquals(expectedValue, result);
+ }
- // call real method on send monthly statement
-
Mockito.doCallRealMethod().when(quotaAlertManager).checkAndSendQuotaAlertEmails();
+ @Test
+ public void
isQuotaEmailTypeEnabledForAccountTestConfigurationIsEnabledAndEmailIsNotConfiguredReturnDefaultValue()
{
+ boolean defaultValue = QuotaConfig.QuotaEnableEmails.value();
+
+ boolean result =
quotaAlertManager.isQuotaEmailTypeEnabledForAccount(accountMock,
QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY);
+
+ Assert.assertEquals(defaultValue, result);
+ }
+
+ @Test
+ public void checkQuotaAlertEmailForAccountTestNullAccountBalance() {
+ Mockito.doReturn(null).when(quotaAccountVOMock).getQuotaBalance();
+
quotaAlertManager.checkQuotaAlertEmailForAccount(deferredQuotaEmailListMock,
quotaAccountVOMock);
+ Mockito.verify(accountDao, Mockito.never()).findById(Mockito.any());
+ }
+
+ @Test
+ public void checkQuotaAlertEmailForAccountTestNullAccount() {
+ Mockito.doReturn(new
BigDecimal(1)).when(quotaAccountVOMock).getQuotaBalance();
+ Mockito.doReturn(null).when(accountDao).findById(Mockito.any());
+
quotaAlertManager.checkQuotaAlertEmailForAccount(deferredQuotaEmailListMock,
quotaAccountVOMock);
+ Mockito.verify(quotaAccountVOMock,
Mockito.never()).getQuotaBalanceDate();
+ }
+
+ @Test
+ public void checkQuotaAlertEmailForAccountTestEnoughBalance() {
+
quotaAlertManager.checkQuotaAlertEmailForAccount(deferredQuotaEmailListMock,
quotaAccountVOMock);
+ Mockito.verify(quotaAlertManager,
Mockito.never()).lockAccount(Mockito.anyLong());
+ Mockito.verify(deferredQuotaEmailListMock,
Mockito.never()).add(Mockito.any());
+ }
+
+ @Test
+ public void
checkQuotaAlertEmailForAccountTestBalanceLowerThanZeroAndLockAccountEnforcementFalse()
{
+ Mockito.doReturn(new
BigDecimal(-1)).when(quotaAccountVOMock).getQuotaBalance();
+
+ quotaAlertManager._lockAccountEnforcement = false;
+ Mockito.doReturn(1).when(quotaAccountVOMock).getQuotaEnforce();
+
quotaAlertManager.checkQuotaAlertEmailForAccount(deferredQuotaEmailListMock,
quotaAccountVOMock);
+ Mockito.verify(quotaAlertManager,
Mockito.never()).lockAccount(Mockito.anyLong());
+ }
+
+ @Test
+ public void
checkQuotaAlertEmailForAccountTestBalanceLowerThanZeroAndLockableFalse() {
+ Mockito.doReturn(new
BigDecimal(-1)).when(quotaAccountVOMock).getQuotaBalance();
+
+ quotaAlertManager._lockAccountEnforcement = true;
+ Mockito.doReturn(1).when(quotaAccountVOMock).getQuotaEnforce();
+
Mockito.doReturn(false).when(quotaManagerMock).isLockable(Mockito.any());
+
quotaAlertManager.checkQuotaAlertEmailForAccount(deferredQuotaEmailListMock,
quotaAccountVOMock);
+ Mockito.verify(quotaAlertManager,
Mockito.never()).lockAccount(Mockito.anyLong());
+ }
+
+ @Test
+ public void
checkQuotaAlertEmailForAccountTestBalanceLowerThanZeroAndIsLockableFalse() {
+ Mockito.doReturn(new
BigDecimal(-1)).when(quotaAccountVOMock).getQuotaBalance();
+
+ quotaAlertManager._lockAccountEnforcement = true;
+ Mockito.doReturn(1).when(quotaAccountVOMock).getQuotaEnforce();
+
Mockito.doReturn(false).when(quotaManagerMock).isLockable(Mockito.any());
+
quotaAlertManager.checkQuotaAlertEmailForAccount(deferredQuotaEmailListMock,
quotaAccountVOMock);
+ Mockito.verify(quotaAlertManager,
Mockito.never()).lockAccount(Mockito.anyLong());
+ }
+
+ @Test
+ public void
checkQuotaAlertEmailForAccountTestBalanceLowerThanZeroAndLockAccount() {
+ Mockito.doReturn(new
BigDecimal(-1)).when(quotaAccountVOMock).getQuotaBalance();
+
+ quotaAlertManager._lockAccountEnforcement = true;
+ Mockito.doReturn(1).when(quotaAccountVOMock).getQuotaEnforce();
+
Mockito.doReturn(true).when(quotaManagerMock).isLockable(Mockito.any());
+
quotaAlertManager.checkQuotaAlertEmailForAccount(deferredQuotaEmailListMock,
quotaAccountVOMock);
+ Mockito.verify(quotaAlertManager).lockAccount(Mockito.anyLong());
+ }
+
+ @Test
+ public void
checkQuotaAlertEmailForAccountTestBalanceLowerThanZeroAndAlertDateNotNullAndBalanceDateNotAfter()
{
+ Mockito.doReturn(new
Date()).when(quotaAccountVOMock).getQuotaAlertDate();
+ Mockito.doReturn(new
BigDecimal(-1)).when(quotaAccountVOMock).getQuotaBalance();
+ Mockito.doReturn(false).when(balanceDateMock).after(Mockito.any());
+
+
quotaAlertManager.checkQuotaAlertEmailForAccount(deferredQuotaEmailListMock,
quotaAccountVOMock);
+ Mockito.verify(deferredQuotaEmailListMock,
Mockito.never()).add(Mockito.any());
+ }
+
+ public void
checkQuotaAlertEmailForAccountTestBalanceLowerThanZeroAndAlertDateNotNullAndGetDifferenceDaysSmallerThanOne()
{
+ Mockito.doReturn(new
Date()).when(quotaAccountVOMock).getQuotaAlertDate();
+ Mockito.doReturn(new
BigDecimal(-1)).when(quotaAccountVOMock).getQuotaBalance();
+ Mockito.doReturn(true).when(balanceDateMock).after(Mockito.any());
+
Mockito.doReturn(0L).when(quotaAlertManager).getDifferenceDays(Mockito.any(),
Mockito.any());
+
+
quotaAlertManager.checkQuotaAlertEmailForAccount(deferredQuotaEmailListMock,
quotaAccountVOMock);
+ Mockito.verify(deferredQuotaEmailListMock,
Mockito.never()).add(Mockito.any());
+ }
+
+ public void
checkQuotaAlertEmailForAccountTestBalanceLowerThanZeroAndAlertDateNotNullAndBalanceAfterAndDifferenceBiggerThanOne()
{
+ Mockito.doReturn(new
Date()).when(quotaAccountVOMock).getQuotaAlertDate();
+ Mockito.doReturn(new
BigDecimal(-1)).when(quotaAccountVOMock).getQuotaBalance();
+ Mockito.doReturn(true).when(balanceDateMock).after(Mockito.any());
+
Mockito.doReturn(2).when(quotaAlertManager).getDifferenceDays(Mockito.any(),
Mockito.any());
+
+
quotaAlertManager.checkQuotaAlertEmailForAccount(deferredQuotaEmailListMock,
quotaAccountVOMock);
+ Mockito.verify(deferredQuotaEmailListMock).add(Mockito.any());
+ }
- // Case1: valid balance, no email should be sent
- quotaAlertManager.checkAndSendQuotaAlertEmails();
- Mockito.verify(quotaAlertManager,
Mockito.times(0)).sendQuotaAlert(Mockito.any(QuotaAlertManagerImpl.DeferredQuotaEmail.class));
+ public void
checkQuotaAlertEmailForAccountTestBalanceLowerThanZeroAndAlertDateNull() {
+ Mockito.doReturn(new
BigDecimal(-1)).when(quotaAccountVOMock).getQuotaBalance();
- // Case2: low balance, email should be sent
- accounts.get(0).setQuotaBalance(new BigDecimal(99));
- //Mockito.when(quotaAcc.listAll()).thenReturn(accounts);
- quotaAlertManager.checkAndSendQuotaAlertEmails();
- Mockito.verify(quotaAlertManager,
Mockito.times(1)).sendQuotaAlert(Mockito.any(QuotaAlertManagerImpl.DeferredQuotaEmail.class));
+
quotaAlertManager.checkQuotaAlertEmailForAccount(deferredQuotaEmailListMock,
quotaAccountVOMock);
+ Mockito.verify(deferredQuotaEmailListMock).add(Mockito.any());
}
@Test
@@ -196,12 +301,12 @@ public class QuotaAlertManagerImplTest extends TestCase {
@Test
public void testGetDifferenceDays() {
Date now = new Date();
- assertTrue(QuotaAlertManagerImpl.getDifferenceDays(now, now) == 0L);
+ assertTrue(quotaAlertManager.getDifferenceDays(now, now) == 0L);
Calendar c = Calendar.getInstance();
c.setTimeZone(TimeZone.getTimeZone("UTC"));
Calendar c2 = (Calendar)c.clone();
c2.add(Calendar.DATE, 1);
- assertEquals(1L, QuotaAlertManagerImpl.getDifferenceDays(c.getTime(),
c2.getTime()));
+ assertEquals(1L, quotaAlertManager.getDifferenceDays(c.getTime(),
c2.getTime()));
}
@Test
diff --git
a/framework/quota/src/test/java/org/apache/cloudstack/quota/QuotaStatementTest.java
b/framework/quota/src/test/java/org/apache/cloudstack/quota/QuotaStatementTest.java
index baf749cd0c9..507834fef41 100644
---
a/framework/quota/src/test/java/org/apache/cloudstack/quota/QuotaStatementTest.java
+++
b/framework/quota/src/test/java/org/apache/cloudstack/quota/QuotaStatementTest.java
@@ -16,7 +16,6 @@
// under the License.
package org.apache.cloudstack.quota;
-import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
@@ -24,17 +23,21 @@ import java.util.Calendar;
import java.util.Date;
import java.util.List;
-import javax.mail.MessagingException;
import javax.naming.ConfigurationException;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.quota.QuotaStatementImpl.QuotaStatementPeriods;
+import org.apache.cloudstack.quota.constant.QuotaConfig;
import org.apache.cloudstack.quota.dao.QuotaAccountDao;
+import org.apache.cloudstack.quota.dao.QuotaEmailConfigurationDaoImpl;
+import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
import org.apache.cloudstack.quota.dao.QuotaUsageDao;
import org.apache.cloudstack.quota.vo.QuotaAccountVO;
+import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
@@ -60,7 +63,20 @@ public class QuotaStatementTest extends TestCase {
@Mock
QuotaAlertManager alertManager;
+ @Mock
+ QuotaEmailConfigurationDaoImpl quotaEmailConfigurationDaoMock;
+
+ @Mock
+ QuotaEmailTemplatesDao quotaEmailTemplatesDaoMock;
+
+ @Mock
+ QuotaEmailTemplatesVO quotaEmailTemplatesVOMock;
+
+ @Mock
+ List<QuotaEmailTemplatesVO> listMock;
+
@Spy
+ @InjectMocks
QuotaStatementImpl quotaStatement = new QuotaStatementImpl();
private void injectMockToField(Object mock, String fieldName) throws
NoSuchFieldException, IllegalAccessException {
@@ -227,7 +243,10 @@ public class QuotaStatementTest extends TestCase {
@Test
- public void testSendStatement() throws UnsupportedEncodingException,
MessagingException {
+ public void sendStatementTestUnconfiguredEmail() {
+ boolean defaultConfigurationValue =
QuotaConfig.QuotaEnableEmails.value();
+
Mockito.doReturn(defaultConfigurationValue).when(alertManager).isQuotaEmailTypeEnabledForAccount(Mockito.any(AccountVO.class),
Mockito.any(QuotaConfig.QuotaEmailTemplateTypes.class));
+
Calendar date = Calendar.getInstance();
AccountVO accountVO = new AccountVO();
accountVO.setId(2L);
@@ -252,4 +271,46 @@ public class QuotaStatementTest extends TestCase {
}
}
+ @Test
+ public void sendStatementTestEnabledEmail() {
+
Mockito.doReturn(true).when(alertManager).isQuotaEmailTypeEnabledForAccount(Mockito.any(AccountVO.class),
Mockito.any(QuotaConfig.QuotaEmailTemplateTypes.class));
+
+ Calendar date = Calendar.getInstance();
+ AccountVO accountVO = new AccountVO();
+ accountVO.setId(2L);
+ accountVO.setDomainId(1L);
+
Mockito.lenient().when(accountDao.findById(Mockito.anyLong())).thenReturn(accountVO);
+
+ QuotaAccountVO acc = new QuotaAccountVO(2L);
+ acc.setQuotaBalance(new BigDecimal(404));
+ acc.setLastStatementDate(null);
+ List<QuotaAccountVO> accounts = new ArrayList<>();
+ accounts.add(acc);
+
Mockito.lenient().when(quotaAcc.listAllQuotaAccount()).thenReturn(accounts);
+
+
Mockito.lenient().when(quotaUsage.findTotalQuotaUsage(Mockito.anyLong(),
Mockito.anyLong(), Mockito.anyInt(), Mockito.any(Date.class),
Mockito.any(Date.class)))
+ .thenReturn(new BigDecimal(100));
+
+ // call real method on send monthly statement
+ quotaStatement.sendStatement();
+ Calendar period[] = quotaStatement.statementTime(date,
QuotaStatementPeriods.MONTHLY);
+ if (period != null){
+ Mockito.verify(alertManager,
Mockito.times(1)).sendQuotaAlert(Mockito.any(QuotaAlertManagerImpl.DeferredQuotaEmail.class));
+ }
+ }
+
+ @Test
+ public void sendStatementTestDisabledEmail() {
+ QuotaAccountVO quotaAccountVoMock = Mockito.mock(QuotaAccountVO.class);
+
Mockito.when(quotaAccountVoMock.getQuotaBalance()).thenReturn(BigDecimal.ONE);
+
Mockito.when(quotaAcc.listAllQuotaAccount()).thenReturn(List.of(quotaAccountVoMock));
+ AccountVO accountVoMock = Mockito.mock(AccountVO.class);
+
Mockito.doReturn(accountVoMock).when(accountDao).findById(Mockito.anyLong());
+
Mockito.doReturn(false).when(alertManager).isQuotaEmailTypeEnabledForAccount(Mockito.any(AccountVO.class),
Mockito.any(QuotaConfig.QuotaEmailTemplateTypes.class));
+
+ quotaStatement.sendStatement();
+
+ Mockito.verify(quotaStatement,
Mockito.never()).statementTime(Mockito.any(), Mockito.any());
+ }
+
}
diff --git
a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaConfigureEmailCmd.java
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaConfigureEmailCmd.java
new file mode 100644
index 00000000000..01d9ffc1529
--- /dev/null
+++
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaConfigureEmailCmd.java
@@ -0,0 +1,79 @@
+//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 = "quotaConfigureEmail", responseObject =
QuotaConfigureEmailResponse.class, description = "Configure a quota email
template", since = "4.20.0.0",
+ requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
+public class QuotaConfigureEmailCmd extends BaseCmd {
+
+ @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 long getEntityOwnerId() {
+ return accountId;
+ }
+
+ public long getAccountId() {
+ return accountId;
+ }
+
+ public String getTemplateName() {
+ return templateName;
+ }
+
+ public Boolean getEnable() {
+ return enable;
+ }
+
+ public Double getMinBalance() {
+ return minBalance;
+ }
+}
diff --git
a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaListEmailConfigurationCmd.java
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaListEmailConfigurationCmd.java
new file mode 100644
index 00000000000..8915158461f
--- /dev/null
+++
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaListEmailConfigurationCmd.java
@@ -0,0 +1,54 @@
+//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 = "quotaListEmailConfiguration", responseObject =
QuotaConfigureEmailResponse.class, description = "List quota email template
configurations", since = "4.20.0.0",
+ requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
+public class QuotaListEmailConfigurationCmd extends BaseCmd {
+
+ @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 long getEntityOwnerId() {
+ return Account.ACCOUNT_ID_SYSTEM;
+ }
+}
diff --git
a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaConfigureEmailResponse.java
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaConfigureEmailResponse.java
new file mode 100644
index 00000000000..4f84a2c2828
--- /dev/null
+++
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaConfigureEmailResponse.java
@@ -0,0 +1,78 @@
+//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.response;
+
+import com.cloud.serializer.Param;
+import com.google.gson.annotations.SerializedName;
+import org.apache.cloudstack.api.BaseResponse;
+
+
+public class QuotaConfigureEmailResponse extends BaseResponse {
+
+ @SerializedName("account")
+ @Param(description = "The configured account's id.")
+ private String accountId;
+
+ @SerializedName("templatename")
+ @Param(description = "The template's name.")
+ private String templateName;
+
+ @SerializedName("enabled")
+ @Param(description = "Whether the template is enabled.")
+ private Boolean enabled;
+
+ @SerializedName("minbalance")
+ @Param(description = "The configured account's min balance.")
+ private Double minBalance;
+
+ public QuotaConfigureEmailResponse() {
+ super("quotaconfigureemail");
+ setResponseName("");
+ }
+
+ public String getAccountId() {
+ return accountId;
+ }
+
+ public void setAccountId(String accountId) {
+ this.accountId = accountId;
+ }
+
+ public String getTemplateName() {
+ return templateName;
+ }
+
+ public void setTemplateName(String templateName) {
+ this.templateName = templateName;
+ }
+
+ public Boolean getEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public Double getMinBalance() {
+ return minBalance;
+ }
+
+ public void setMinBalance(Double minBalance) {
+ this.minBalance = minBalance;
+ }
+}
diff --git
a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilder.java
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilder.java
index 36033043bcf..57aa04e00fa 100644
---
a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilder.java
+++
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilder.java
@@ -17,6 +17,7 @@
package org.apache.cloudstack.api.response;
import org.apache.cloudstack.api.command.QuotaBalanceCmd;
+import org.apache.cloudstack.api.command.QuotaConfigureEmailCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
import org.apache.cloudstack.api.command.QuotaStatementCmd;
@@ -24,6 +25,7 @@ import org.apache.cloudstack.api.command.QuotaTariffCreateCmd;
import org.apache.cloudstack.api.command.QuotaTariffListCmd;
import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
+import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
import org.apache.cloudstack.quota.vo.QuotaUsageVO;
@@ -69,4 +71,10 @@ public interface QuotaResponseBuilder {
QuotaTariffVO createQuotaTariff(QuotaTariffCreateCmd cmd);
boolean deleteQuotaTariff(String quotaTariffUuid);
+
+ Pair<QuotaEmailConfigurationVO, Double>
configureQuotaEmail(QuotaConfigureEmailCmd cmd);
+
+ QuotaConfigureEmailResponse
createQuotaConfigureEmailResponse(QuotaEmailConfigurationVO
quotaEmailConfigurationVO, Double minBalance, long accountId);
+
+ List<QuotaConfigureEmailResponse> listEmailConfiguration(long accountId);
}
diff --git
a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java
index d4996887a40..94897b410f4 100644
---
a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java
+++
b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java
@@ -38,6 +38,7 @@ import com.cloud.utils.DateUtil;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.command.QuotaBalanceCmd;
+import org.apache.cloudstack.api.command.QuotaConfigureEmailCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
import org.apache.cloudstack.api.command.QuotaStatementCmd;
@@ -54,12 +55,14 @@ import org.apache.cloudstack.quota.constant.QuotaTypes;
import org.apache.cloudstack.quota.dao.QuotaAccountDao;
import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
+import org.apache.cloudstack.quota.dao.QuotaEmailConfigurationDao;
import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
import org.apache.cloudstack.quota.dao.QuotaTariffDao;
-import org.apache.cloudstack.quota.dao.QuotaUsageDao;
import org.apache.cloudstack.quota.vo.QuotaAccountVO;
+import org.apache.cloudstack.quota.dao.QuotaUsageDao;
import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
+import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
import org.apache.cloudstack.quota.vo.QuotaUsageVO;
@@ -104,7 +107,7 @@ public class QuotaResponseBuilderImpl implements
QuotaResponseBuilder {
@Inject
private AccountDao _accountDao;
@Inject
- private QuotaAccountDao _quotaAccountDao;
+ private QuotaAccountDao quotaAccountDao;
@Inject
private DomainDao _domainDao;
@Inject
@@ -113,6 +116,8 @@ public class QuotaResponseBuilderImpl implements
QuotaResponseBuilder {
private QuotaStatement _statement;
@Inject
private QuotaManager _quotaManager;
+ @Inject
+ private QuotaEmailConfigurationDao quotaEmailConfigurationDao;
@Override
public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff)
{
@@ -165,7 +170,7 @@ public class QuotaResponseBuilderImpl implements
QuotaResponseBuilder {
result.add(qr);
}
} else {
- Pair<List<QuotaAccountVO>, Integer> data =
_quotaAccountDao.listAllQuotaAccount(startIndex, pageSize);
+ Pair<List<QuotaAccountVO>, Integer> data =
quotaAccountDao.listAllQuotaAccount(startIndex, pageSize);
count = data.second();
for (final QuotaAccountVO quotaAccount : data.first()) {
AccountVO account = _accountDao.findById(quotaAccount.getId());
@@ -676,4 +681,99 @@ public class QuotaResponseBuilderImpl implements
QuotaResponseBuilder {
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());
+ }
+
+ QuotaEmailConfigurationVO configurationVO =
getQuotaEmailConfigurationVo(cmd);
+ return new Pair<>(configurationVO, minBalance);
+ }
+
+ protected QuotaEmailConfigurationVO
getQuotaEmailConfigurationVo(QuotaConfigureEmailCmd cmd) {
+ if (cmd.getTemplateName() == null) {
+ return 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 configurationVO;
+ }
+
+ configurationVO.setEnabled(cmd.getEnable());
+ return
quotaEmailConfigurationDao.updateQuotaEmailConfiguration(configurationVO);
+ }
+
+ protected void
validateQuotaConfigureEmailCmdParameters(QuotaConfigureEmailCmd cmd) {
+ if (quotaAccountDao.findByIdQuotaAccount(cmd.getAccountId()) == null) {
+ throw new InvalidParameterValueException("You must have the quota
enabled for this account to configure quota emails.");
+ }
+
+ if (cmd.getTemplateName() == null && cmd.getMinBalance() == null) {
+ throw new InvalidParameterValueException("You should inform at
least the 'minbalance' or both the 'templatename' and 'enable' parameters.");
+ }
+
+ if ((cmd.getTemplateName() != null && cmd.getEnable() == null) ||
(cmd.getTemplateName() == null && cmd.getEnable() != null)) {
+ throw new InvalidParameterValueException("Parameter 'enable' must
be informed along with 'templatename'.");
+ }
+ }
+
+ public QuotaConfigureEmailResponse
createQuotaConfigureEmailResponse(QuotaEmailConfigurationVO
quotaEmailConfigurationVO, Double minBalance, long accountId) {
+ QuotaConfigureEmailResponse quotaConfigureEmailResponse = new
QuotaConfigureEmailResponse();
+
+ Account account = _accountDao.findByIdIncludingRemoved(accountId);
+ if (quotaEmailConfigurationVO != null) {
+ QuotaEmailTemplatesVO templateVO =
_quotaEmailTemplateDao.findById(quotaEmailConfigurationVO.getEmailTemplateId());
+
+ quotaConfigureEmailResponse.setAccountId(account.getUuid());
+
quotaConfigureEmailResponse.setTemplateName(templateVO.getTemplateName());
+
quotaConfigureEmailResponse.setEnabled(quotaEmailConfigurationVO.isEnabled());
+ }
+
+ quotaConfigureEmailResponse.setMinBalance(minBalance);
+
+ return quotaConfigureEmailResponse;
+ }
+
+ @Override
+ public List<QuotaConfigureEmailResponse> listEmailConfiguration(long
accountId) {
+ List<QuotaEmailConfigurationVO> emailConfigurationVOList =
quotaEmailConfigurationDao.listByAccount(accountId);
+ Account account = _accountDao.findById(accountId);
+ QuotaAccountVO quotaAccountVO =
quotaAccountDao.findByIdQuotaAccount(accountId);
+
+ List<QuotaConfigureEmailResponse> quotaConfigureEmailResponseList =
new ArrayList<>();
+ for (QuotaEmailConfigurationVO quotaEmailConfigurationVO :
emailConfigurationVOList) {
+
quotaConfigureEmailResponseList.add(createQuotaConfigureEmailResponse(quotaEmailConfigurationVO,
account, quotaAccountVO));
+ }
+
+ return quotaConfigureEmailResponseList;
+ }
+
+ protected QuotaConfigureEmailResponse
createQuotaConfigureEmailResponse(QuotaEmailConfigurationVO
quotaEmailConfigurationVO, Account account, QuotaAccountVO quotaAccountVO) {
+ QuotaConfigureEmailResponse quotaConfigureEmailResponse = new
QuotaConfigureEmailResponse();
+
+ QuotaEmailTemplatesVO templateVO =
_quotaEmailTemplateDao.findById(quotaEmailConfigurationVO.getEmailTemplateId());
+
+ quotaConfigureEmailResponse.setAccountId(account.getUuid());
+
quotaConfigureEmailResponse.setTemplateName(templateVO.getTemplateName());
+
quotaConfigureEmailResponse.setEnabled(quotaEmailConfigurationVO.isEnabled());
+
+
quotaConfigureEmailResponse.setMinBalance(quotaAccountVO.getQuotaMinBalance().doubleValue());
+
+ return quotaConfigureEmailResponse;
+ }
}
diff --git
a/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java
b/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java
index 88a69c47e05..da3f50b165a 100644
---
a/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java
+++
b/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java
@@ -28,10 +28,12 @@ import javax.inject.Inject;
import javax.naming.ConfigurationException;
import org.apache.cloudstack.api.command.QuotaBalanceCmd;
+import org.apache.cloudstack.api.command.QuotaConfigureEmailCmd;
import org.apache.cloudstack.api.command.QuotaCreditsCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
import org.apache.cloudstack.api.command.QuotaEnabledCmd;
+import org.apache.cloudstack.api.command.QuotaListEmailConfigurationCmd;
import org.apache.cloudstack.api.command.QuotaStatementCmd;
import org.apache.cloudstack.api.command.QuotaSummaryCmd;
import org.apache.cloudstack.api.command.QuotaTariffCreateCmd;
@@ -116,6 +118,8 @@ public class QuotaServiceImpl extends ManagerBase
implements QuotaService, Confi
cmdList.add(QuotaEmailTemplateUpdateCmd.class);
cmdList.add(QuotaTariffCreateCmd.class);
cmdList.add(QuotaTariffDeleteCmd.class);
+ cmdList.add(QuotaConfigureEmailCmd.class);
+ cmdList.add(QuotaListEmailConfigurationCmd.class);
return cmdList;
}
@@ -128,7 +132,7 @@ public class QuotaServiceImpl extends ManagerBase
implements QuotaService, Confi
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {QuotaPluginEnabled, QuotaEnableEnforcement,
QuotaCurrencySymbol, QuotaCurrencyLocale, QuotaStatementPeriod, QuotaSmtpHost,
QuotaSmtpPort, QuotaSmtpTimeout,
QuotaSmtpUser, QuotaSmtpPassword, QuotaSmtpAuthType,
QuotaSmtpSender, QuotaSmtpEnabledSecurityProtocols, QuotaSmtpUseStartTLS,
QuotaActivationRuleTimeout, QuotaAccountEnabled,
- QuotaEmailHeader, QuotaEmailFooter};
+ QuotaEmailHeader, QuotaEmailFooter, QuotaEnableEmails};
}
@Override
diff --git
a/plugins/database/quota/src/test/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java
b/plugins/database/quota/src/test/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java
index b960a1be569..899ce649fce 100644
---
a/plugins/database/quota/src/test/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java
+++
b/plugins/database/quota/src/test/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImplTest.java
@@ -30,6 +30,7 @@ import java.util.function.Consumer;
import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao;
import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.command.QuotaConfigureEmailCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
import org.apache.cloudstack.framework.config.ConfigKey;
@@ -37,13 +38,17 @@ import org.apache.cloudstack.quota.QuotaService;
import org.apache.cloudstack.quota.QuotaStatement;
import org.apache.cloudstack.quota.constant.QuotaConfig;
import org.apache.cloudstack.quota.constant.QuotaTypes;
+import org.apache.cloudstack.quota.dao.QuotaAccountDao;
import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
import org.apache.cloudstack.quota.dao.QuotaCreditsDao;
+import org.apache.cloudstack.quota.dao.QuotaEmailConfigurationDao;
import org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDao;
import org.apache.cloudstack.quota.dao.QuotaTariffDao;
import org.apache.cloudstack.quota.dao.QuotaUsageDao;
+import org.apache.cloudstack.quota.vo.QuotaAccountVO;
import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
+import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
import org.apache.commons.lang3.time.DateUtils;
@@ -103,6 +108,12 @@ public class QuotaResponseBuilderImplTest extends TestCase
{
@Mock
QuotaUsageDao quotaUsageDaoMock;
+ @Mock
+ QuotaAccountDao quotaAccountDaoMock;
+
+ @Mock
+ QuotaEmailConfigurationDao quotaEmailConfigurationDaoMock;
+
@InjectMocks
QuotaResponseBuilderImpl quotaResponseBuilderSpy =
Mockito.spy(QuotaResponseBuilderImpl.class);
@@ -114,6 +125,15 @@ public class QuotaResponseBuilderImplTest extends TestCase
{
@Mock
DomainVO domainVOMock;
+ @Mock
+ QuotaConfigureEmailCmd quotaConfigureEmailCmdMock;
+
+ @Mock
+ QuotaAccountVO quotaAccountVOMock;
+
+ @Mock
+ QuotaEmailTemplatesVO quotaEmailTemplatesVoMock;
+
private void overrideDefaultQuotaEnabledConfigValue(final Object value)
throws IllegalAccessException, NoSuchFieldException {
Field f = ConfigKey.class.getDeclaredField("_defaultValue");
f.setAccessible(true);
@@ -403,4 +423,96 @@ public class QuotaResponseBuilderImplTest extends TestCase
{
assertTrue(quotaSummaryResponse.getQuotaEnabled());
}
+
+
+ @Test (expected = InvalidParameterValueException.class)
+ public void validateQuotaConfigureEmailCmdParametersTestNullQuotaAccount()
{
+
Mockito.doReturn(null).when(quotaAccountDaoMock).findByIdQuotaAccount(Mockito.any());
+
quotaResponseBuilderSpy.validateQuotaConfigureEmailCmdParameters(quotaConfigureEmailCmdMock);
+ }
+
+ @Test (expected = InvalidParameterValueException.class)
+ public void
validateQuotaConfigureEmailCmdParametersTestNullTemplateNameAndMinBalance() {
+
Mockito.doReturn(quotaAccountVOMock).when(quotaAccountDaoMock).findByIdQuotaAccount(Mockito.any());
+
Mockito.doReturn(null).when(quotaConfigureEmailCmdMock).getTemplateName();
+
Mockito.doReturn(null).when(quotaConfigureEmailCmdMock).getMinBalance();
+
quotaResponseBuilderSpy.validateQuotaConfigureEmailCmdParameters(quotaConfigureEmailCmdMock);
+ }
+
+ @Test (expected = InvalidParameterValueException.class)
+ public void
validateQuotaConfigureEmailCmdParametersTestEnableNullAndTemplateNameNotNull() {
+
Mockito.doReturn(quotaAccountVOMock).when(quotaAccountDaoMock).findByIdQuotaAccount(Mockito.any());
+
Mockito.doReturn(QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW.toString()).when(quotaConfigureEmailCmdMock).getTemplateName();
+ Mockito.doReturn(null).when(quotaConfigureEmailCmdMock).getEnable();
+
quotaResponseBuilderSpy.validateQuotaConfigureEmailCmdParameters(quotaConfigureEmailCmdMock);
+ }
+
+
+ @Test
+ public void validateQuotaConfigureEmailCmdParametersTestNullTemplateName()
{
+
Mockito.doReturn(quotaAccountVOMock).when(quotaAccountDaoMock).findByIdQuotaAccount(Mockito.any());
+
Mockito.doReturn(null).when(quotaConfigureEmailCmdMock).getTemplateName();
+ Mockito.doReturn(null).when(quotaConfigureEmailCmdMock).getEnable();
+
Mockito.doReturn(100D).when(quotaConfigureEmailCmdMock).getMinBalance();
+
quotaResponseBuilderSpy.validateQuotaConfigureEmailCmdParameters(quotaConfigureEmailCmdMock);
+ }
+
+ @Test
+ public void
validateQuotaConfigureEmailCmdParametersTestWithTemplateNameAndEnable() {
+
Mockito.doReturn(quotaAccountVOMock).when(quotaAccountDaoMock).findByIdQuotaAccount(Mockito.any());
+
Mockito.doReturn(QuotaConfig.QuotaEmailTemplateTypes.QUOTA_LOW.toString()).when(quotaConfigureEmailCmdMock).getTemplateName();
+ Mockito.doReturn(true).when(quotaConfigureEmailCmdMock).getEnable();
+
quotaResponseBuilderSpy.validateQuotaConfigureEmailCmdParameters(quotaConfigureEmailCmdMock);
+ }
+
+ @Test
+ public void getQuotaEmailConfigurationVoTestTemplateNameIsNull() {
+
Mockito.doReturn(null).when(quotaConfigureEmailCmdMock).getTemplateName();
+
+ QuotaEmailConfigurationVO result =
quotaResponseBuilderSpy.getQuotaEmailConfigurationVo(quotaConfigureEmailCmdMock);
+
+ Assert.assertNull(result);
+ }
+
+ @Test (expected = InvalidParameterValueException.class)
+ public void getQuotaEmailConfigurationVoTestNoTemplateFound() {
+
Mockito.doReturn("name").when(quotaConfigureEmailCmdMock).getTemplateName();
+ Mockito.doReturn(new
ArrayList<QuotaEmailTemplatesVO>()).when(quotaEmailTemplateDaoMock).listAllQuotaEmailTemplates(Mockito.any());
+
+
quotaResponseBuilderSpy.getQuotaEmailConfigurationVo(quotaConfigureEmailCmdMock);
+ }
+
+ @Test
+ public void getQuotaEmailConfigurationVoTestNewConfiguration() {
+
Mockito.doReturn("name").when(quotaConfigureEmailCmdMock).getTemplateName();
+ List<QuotaEmailTemplatesVO> templatesVOArrayList =
List.of(quotaEmailTemplatesVoMock);
+
Mockito.doReturn(templatesVOArrayList).when(quotaEmailTemplateDaoMock).listAllQuotaEmailTemplates(Mockito.any());
+
Mockito.doReturn(null).when(quotaEmailConfigurationDaoMock).findByAccountIdAndEmailTemplateId(Mockito.anyLong(),
Mockito.anyLong());
+
+ QuotaEmailConfigurationVO result =
quotaResponseBuilderSpy.getQuotaEmailConfigurationVo(quotaConfigureEmailCmdMock);
+
+
Mockito.verify(quotaEmailConfigurationDaoMock).persistQuotaEmailConfiguration(Mockito.any());
+ assertEquals(0, result.getAccountId());
+ assertEquals(0, result.getEmailTemplateId());
+ assertFalse(result.isEnabled());
+ }
+
+ @Test
+ public void getQuotaEmailConfigurationVoTestExistingConfiguration() {
+
Mockito.doReturn("name").when(quotaConfigureEmailCmdMock).getTemplateName();
+ List<QuotaEmailTemplatesVO> templatesVOArrayList =
List.of(quotaEmailTemplatesVoMock);
+
Mockito.doReturn(templatesVOArrayList).when(quotaEmailTemplateDaoMock).listAllQuotaEmailTemplates(Mockito.any());
+
+ QuotaEmailConfigurationVO quotaEmailConfigurationVO = new
QuotaEmailConfigurationVO(1, 2, true);
+
Mockito.doReturn(quotaEmailConfigurationVO).when(quotaEmailConfigurationDaoMock).findByAccountIdAndEmailTemplateId(Mockito.anyLong(),
Mockito.anyLong());
+
Mockito.doReturn(quotaEmailConfigurationVO).when(quotaEmailConfigurationDaoMock).updateQuotaEmailConfiguration(Mockito.any());
+
+ QuotaEmailConfigurationVO result =
quotaResponseBuilderSpy.getQuotaEmailConfigurationVo(quotaConfigureEmailCmdMock);
+
+
Mockito.verify(quotaEmailConfigurationDaoMock).updateQuotaEmailConfiguration(Mockito.any());
+
+ assertEquals(1, result.getAccountId());
+ assertEquals(2, result.getEmailTemplateId());
+ assertFalse(result.isEnabled());
+ }
}