Quota email configuration (#8307)

* Quota email configuration feature
This commit is contained in:
João Jandre 2024-03-18 08:26:41 -03:00 committed by GitHub
parent 7b02c4cc48
commit f76c6f3ea9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 987 additions and 90 deletions

View File

@ -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.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','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"'); 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`));

View File

@ -16,11 +16,14 @@
//under the License. //under the License.
package org.apache.cloudstack.quota; package org.apache.cloudstack.quota;
import com.cloud.user.AccountVO;
import com.cloud.utils.component.Manager; import com.cloud.utils.component.Manager;
import org.apache.cloudstack.quota.QuotaAlertManagerImpl.DeferredQuotaEmail; import org.apache.cloudstack.quota.QuotaAlertManagerImpl.DeferredQuotaEmail;
import org.apache.cloudstack.quota.constant.QuotaConfig;
public interface QuotaAlertManager extends Manager { public interface QuotaAlertManager extends Manager {
boolean isQuotaEmailTypeEnabledForAccount(AccountVO account, QuotaConfig.QuotaEmailTemplateTypes quotaEmailTemplateType);
void checkAndSendQuotaAlertEmails(); void checkAndSendQuotaAlertEmails();
void sendQuotaAlert(DeferredQuotaEmail emailToBeSent); void sendQuotaAlert(DeferredQuotaEmail emailToBeSent);
} }

View File

@ -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;
import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes; import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes;
import org.apache.cloudstack.quota.dao.QuotaAccountDao; 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.QuotaEmailTemplatesDao;
import org.apache.cloudstack.quota.vo.QuotaAccountVO; import org.apache.cloudstack.quota.vo.QuotaAccountVO;
import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO; import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.text.StrSubstitutor; import org.apache.commons.lang.text.StrSubstitutor;
@ -80,7 +82,10 @@ public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertMana
@Inject @Inject
private QuotaManager _quotaManager; private QuotaManager _quotaManager;
private boolean _lockAccountEnforcement = false; @Inject
private QuotaEmailConfigurationDao quotaEmailConfigurationDao;
protected boolean _lockAccountEnforcement = false;
private String senderAddress; private String senderAddress;
protected SMTPMailSender mailSender; protected SMTPMailSender mailSender;
@ -139,55 +144,100 @@ public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertMana
return true; 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 @Override
public void checkAndSendQuotaAlertEmails() { public void checkAndSendQuotaAlertEmails() {
List<DeferredQuotaEmail> deferredQuotaEmailList = new ArrayList<DeferredQuotaEmail>(); 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()) { for (final QuotaAccountVO quotaAccount : _quotaAcc.listAllQuotaAccount()) {
if (logger.isDebugEnabled()) { checkQuotaAlertEmailForAccount(deferredQuotaEmailList, quotaAccount);
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));
}
}
}
} }
for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) { for (DeferredQuotaEmail emailToBeSent : deferredQuotaEmailList) {
if (logger.isDebugEnabled()) { logger.debug("Attempting to send a quota alert email to users of account [{}].", emailToBeSent.getAccount().getAccountName());
logger.debug("checkAndSendQuotaAlertEmails: Attempting to send quota alert email to users of account: " + emailToBeSent.getAccount().getAccountName());
}
sendQuotaAlert(emailToBeSent); 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 @Override
public void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) { public void sendQuotaAlert(DeferredQuotaEmail emailToBeSent) {
final AccountVO account = emailToBeSent.getAccount(); final AccountVO account = emailToBeSent.getAccount();
@ -285,7 +335,7 @@ public class QuotaAlertManagerImpl extends ManagerBase implements QuotaAlertMana
return optionMap; return optionMap;
} }
public static long getDifferenceDays(Date d1, Date d2) { public long getDifferenceDays(Date d1, Date d2) {
long diff = d2.getTime() - d1.getTime(); long diff = d2.getTime() - d1.getTime();
return TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS); return TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
} }

View File

@ -31,6 +31,8 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.quota.QuotaAlertManagerImpl.DeferredQuotaEmail; import org.apache.cloudstack.quota.QuotaAlertManagerImpl.DeferredQuotaEmail;
import org.apache.cloudstack.quota.constant.QuotaConfig; import org.apache.cloudstack.quota.constant.QuotaConfig;
import org.apache.cloudstack.quota.dao.QuotaAccountDao; 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.dao.QuotaUsageDao;
import org.apache.cloudstack.quota.vo.QuotaAccountVO; import org.apache.cloudstack.quota.vo.QuotaAccountVO;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -53,6 +55,12 @@ public class QuotaStatementImpl extends ManagerBase implements QuotaStatement {
@Inject @Inject
private ConfigurationDao _configDao; 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 final public static int s_LAST_STATEMENT_SENT_DAYS = 6; //ideally should be less than 7 days
public enum QuotaStatementPeriods { public enum QuotaStatementPeriods {
@ -111,29 +119,34 @@ public class QuotaStatementImpl extends ManagerBase implements QuotaStatement {
if (quotaAccount.getQuotaBalance() == null) { if (quotaAccount.getQuotaBalance() == null) {
continue; // no quota usage for this account ever, ignore 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 //check if it is statement time
Calendar interval[] = statementTime(Calendar.getInstance(), _period); Calendar interval[] = statementTime(Calendar.getInstance(), _period);
Date lastStatementDate = quotaAccount.getLastStatementDate(); Date lastStatementDate = quotaAccount.getLastStatementDate();
if (interval != null) { if (interval != null) {
AccountVO account = _accountDao.findById(quotaAccount.getId()); if (lastStatementDate == null || getDifferenceDays(lastStatementDate, new Date()) >= s_LAST_STATEMENT_SENT_DAYS + 1) {
if (account != null) { BigDecimal quotaUsage = _quotaUsage.findTotalQuotaUsage(account.getAccountId(), account.getDomainId(), null, interval[0].getTime(), interval[1].getTime());
if (lastStatementDate == null || getDifferenceDays(lastStatementDate, new Date()) >= s_LAST_STATEMENT_SENT_DAYS + 1) { logger.info("Quota statement for account [{}] has an usage of [{}].", quotaAccount, quotaUsage);
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");
} // 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) { } else if (lastStatementDate != null) {
logger.info("For " + quotaAccount.getId() + " it is already more than " + getDifferenceDays(lastStatementDate, new Date()) logger.info("For account {} it is already more than {} days, will send statement in next cycle.", quotaAccount.getId(), getDifferenceDays(lastStatementDate, new Date()));
+ " days, will send statement in next cycle");
} }
} }

View File

@ -72,6 +72,9 @@ public interface QuotaConfig {
ConfigKey<String> QuotaEmailFooter = new ConfigKey<>("Advanced", String.class, "quota.email.footer", "", 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); "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 { enum QuotaEmailTemplateTypes {
QUOTA_LOW, QUOTA_EMPTY, QUOTA_UNLOCK_ACCOUNT, QUOTA_STATEMENT QUOTA_LOW, QUOTA_EMPTY, QUOTA_UNLOCK_ACCOUNT, QUOTA_STATEMENT
} }

View File

@ -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);
}

View File

@ -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));
}
}

View File

@ -24,4 +24,6 @@ import java.util.List;
public interface QuotaEmailTemplatesDao extends GenericDao<QuotaEmailTemplatesVO, Long> { public interface QuotaEmailTemplatesDao extends GenericDao<QuotaEmailTemplatesVO, Long> {
List<QuotaEmailTemplatesVO> listAllQuotaEmailTemplates(String templateName); List<QuotaEmailTemplatesVO> listAllQuotaEmailTemplates(String templateName);
boolean updateQuotaEmailTemplate(QuotaEmailTemplatesVO template); boolean updateQuotaEmailTemplate(QuotaEmailTemplatesVO template);
QuotaEmailTemplatesVO findById(long id);
} }

View File

@ -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));
}
} }

View File

@ -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;
}
}

View File

@ -30,5 +30,6 @@
<bean id="QuotaManager" class="org.apache.cloudstack.quota.QuotaManagerImpl" /> <bean id="QuotaManager" class="org.apache.cloudstack.quota.QuotaManagerImpl" />
<bean id="QuotaAlertManager" class="org.apache.cloudstack.quota.QuotaAlertManagerImpl" /> <bean id="QuotaAlertManager" class="org.apache.cloudstack.quota.QuotaAlertManagerImpl" />
<bean id="QuotaStatement" class="org.apache.cloudstack.quota.QuotaStatementImpl" /> <bean id="QuotaStatement" class="org.apache.cloudstack.quota.QuotaStatementImpl" />
<bean id="QuotaEmailConfigurationDao" class="org.apache.cloudstack.quota.dao.QuotaEmailConfigurationDaoImpl"/>
</beans> </beans>

View File

@ -30,9 +30,12 @@ import javax.naming.ConfigurationException;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.quota.constant.QuotaConfig; import org.apache.cloudstack.quota.constant.QuotaConfig;
import org.apache.cloudstack.quota.dao.QuotaAccountDao; 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.QuotaEmailTemplatesDao;
import org.apache.cloudstack.quota.vo.QuotaAccountVO; import org.apache.cloudstack.quota.vo.QuotaAccountVO;
import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO; import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -71,6 +74,9 @@ public class QuotaAlertManagerImplTest extends TestCase {
@Mock @Mock
private ConfigurationDao configDao; private ConfigurationDao configDao;
@Mock
private QuotaEmailConfigurationDaoImpl quotaEmailConfigurationDaoMock;
@Mock @Mock
private QuotaAccountVO quotaAccountVOMock; private QuotaAccountVO quotaAccountVOMock;
@ -92,43 +98,142 @@ public class QuotaAlertManagerImplTest extends TestCase {
@Before @Before
public void setup() throws IllegalAccessException, NoSuchFieldException, ConfigurationException { public void setup() throws IllegalAccessException, NoSuchFieldException, ConfigurationException {
TransactionLegacy.open("QuotaAlertManagerImplTest");
}
@Test
public void testCheckAndSendQuotaAlertEmails() {
AccountVO accountVO = new AccountVO(); AccountVO accountVO = new AccountVO();
accountVO.setId(2L); accountVO.setId(2L);
accountVO.setDomainId(1L); accountVO.setDomainId(1L);
accountVO.setType(Account.Type.NORMAL); accountVO.setType(Account.Type.NORMAL);
Mockito.when(accountDao.findById(Mockito.anyLong())).thenReturn(accountVO); Mockito.when(accountDao.findById(Mockito.anyLong())).thenReturn(accountVO);
QuotaAccountVO acc = new QuotaAccountVO(2L); Mockito.doReturn(new BigDecimal(404)).when(quotaAccountVOMock).getQuotaBalance();
acc.setQuotaBalance(new BigDecimal(404)); Mockito.doReturn(new BigDecimal(100)).when(quotaAccountVOMock).getQuotaMinBalance();
acc.setQuotaMinBalance(new BigDecimal(100)); Mockito.doReturn(balanceDateMock).when(quotaAccountVOMock).getQuotaBalanceDate();
acc.setQuotaBalanceDate(new Date()); Mockito.doReturn(null).when(quotaAccountVOMock).getQuotaAlertDate();
acc.setQuotaAlertDate(null); Mockito.doReturn(0).when(quotaAccountVOMock).getQuotaEnforce();
acc.setQuotaEnforce(0);
List<QuotaAccountVO> accounts = new ArrayList<>();
accounts.add(acc);
Mockito.when(quotaAcc.listAllQuotaAccount()).thenReturn(accounts);
// Don't test sendQuotaAlert yet TransactionLegacy.open("QuotaAlertManagerImplTest");
Mockito.doNothing().when(quotaAlertManager).sendQuotaAlert(Mockito.any(QuotaAlertManagerImpl.DeferredQuotaEmail.class)); }
Mockito.lenient().doReturn(true).when(quotaAlertManager).lockAccount(Mockito.anyLong());
// call real method on send monthly statement @Test
Mockito.doCallRealMethod().when(quotaAlertManager).checkAndSendQuotaAlertEmails(); 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));
// Case1: valid balance, no email should be sent boolean result = quotaAlertManager.isQuotaEmailTypeEnabledForAccount(accountMock, QuotaConfig.QuotaEmailTemplateTypes.QUOTA_EMPTY);
quotaAlertManager.checkAndSendQuotaAlertEmails();
Mockito.verify(quotaAlertManager, Mockito.times(0)).sendQuotaAlert(Mockito.any(QuotaAlertManagerImpl.DeferredQuotaEmail.class));
// Case2: low balance, email should be sent Assert.assertEquals(expectedValue, result);
accounts.get(0).setQuotaBalance(new BigDecimal(99)); }
//Mockito.when(quotaAcc.listAll()).thenReturn(accounts);
quotaAlertManager.checkAndSendQuotaAlertEmails(); @Test
Mockito.verify(quotaAlertManager, Mockito.times(1)).sendQuotaAlert(Mockito.any(QuotaAlertManagerImpl.DeferredQuotaEmail.class)); 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());
}
public void checkQuotaAlertEmailForAccountTestBalanceLowerThanZeroAndAlertDateNull() {
Mockito.doReturn(new BigDecimal(-1)).when(quotaAccountVOMock).getQuotaBalance();
quotaAlertManager.checkQuotaAlertEmailForAccount(deferredQuotaEmailListMock, quotaAccountVOMock);
Mockito.verify(deferredQuotaEmailListMock).add(Mockito.any());
} }
@Test @Test
@ -196,12 +301,12 @@ public class QuotaAlertManagerImplTest extends TestCase {
@Test @Test
public void testGetDifferenceDays() { public void testGetDifferenceDays() {
Date now = new Date(); Date now = new Date();
assertTrue(QuotaAlertManagerImpl.getDifferenceDays(now, now) == 0L); assertTrue(quotaAlertManager.getDifferenceDays(now, now) == 0L);
Calendar c = Calendar.getInstance(); Calendar c = Calendar.getInstance();
c.setTimeZone(TimeZone.getTimeZone("UTC")); c.setTimeZone(TimeZone.getTimeZone("UTC"));
Calendar c2 = (Calendar)c.clone(); Calendar c2 = (Calendar)c.clone();
c2.add(Calendar.DATE, 1); c2.add(Calendar.DATE, 1);
assertEquals(1L, QuotaAlertManagerImpl.getDifferenceDays(c.getTime(), c2.getTime())); assertEquals(1L, quotaAlertManager.getDifferenceDays(c.getTime(), c2.getTime()));
} }
@Test @Test

View File

@ -16,7 +16,6 @@
// under the License. // under the License.
package org.apache.cloudstack.quota; package org.apache.cloudstack.quota;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
@ -24,17 +23,21 @@ import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import javax.mail.MessagingException;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.quota.QuotaStatementImpl.QuotaStatementPeriods; 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.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.dao.QuotaUsageDao;
import org.apache.cloudstack.quota.vo.QuotaAccountVO; import org.apache.cloudstack.quota.vo.QuotaAccountVO;
import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.Spy; import org.mockito.Spy;
@ -60,7 +63,20 @@ public class QuotaStatementTest extends TestCase {
@Mock @Mock
QuotaAlertManager alertManager; QuotaAlertManager alertManager;
@Mock
QuotaEmailConfigurationDaoImpl quotaEmailConfigurationDaoMock;
@Mock
QuotaEmailTemplatesDao quotaEmailTemplatesDaoMock;
@Mock
QuotaEmailTemplatesVO quotaEmailTemplatesVOMock;
@Mock
List<QuotaEmailTemplatesVO> listMock;
@Spy @Spy
@InjectMocks
QuotaStatementImpl quotaStatement = new QuotaStatementImpl(); QuotaStatementImpl quotaStatement = new QuotaStatementImpl();
private void injectMockToField(Object mock, String fieldName) throws NoSuchFieldException, IllegalAccessException { private void injectMockToField(Object mock, String fieldName) throws NoSuchFieldException, IllegalAccessException {
@ -227,7 +243,10 @@ public class QuotaStatementTest extends TestCase {
@Test @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(); Calendar date = Calendar.getInstance();
AccountVO accountVO = new AccountVO(); AccountVO accountVO = new AccountVO();
accountVO.setId(2L); 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());
}
} }

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -17,6 +17,7 @@
package org.apache.cloudstack.api.response; package org.apache.cloudstack.api.response;
import org.apache.cloudstack.api.command.QuotaBalanceCmd; 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.QuotaEmailTemplateListCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd; import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
import org.apache.cloudstack.api.command.QuotaStatementCmd; 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.QuotaTariffListCmd;
import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd; import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd;
import org.apache.cloudstack.quota.vo.QuotaBalanceVO; 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.QuotaTariffVO;
import org.apache.cloudstack.quota.vo.QuotaUsageVO; import org.apache.cloudstack.quota.vo.QuotaUsageVO;
@ -69,4 +71,10 @@ public interface QuotaResponseBuilder {
QuotaTariffVO createQuotaTariff(QuotaTariffCreateCmd cmd); QuotaTariffVO createQuotaTariff(QuotaTariffCreateCmd cmd);
boolean deleteQuotaTariff(String quotaTariffUuid); boolean deleteQuotaTariff(String quotaTariffUuid);
Pair<QuotaEmailConfigurationVO, Double> configureQuotaEmail(QuotaConfigureEmailCmd cmd);
QuotaConfigureEmailResponse createQuotaConfigureEmailResponse(QuotaEmailConfigurationVO quotaEmailConfigurationVO, Double minBalance, long accountId);
List<QuotaConfigureEmailResponse> listEmailConfiguration(long accountId);
} }

View File

@ -38,6 +38,7 @@ import com.cloud.utils.DateUtil;
import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.command.QuotaBalanceCmd; 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.QuotaEmailTemplateListCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd; import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
import org.apache.cloudstack.api.command.QuotaStatementCmd; 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.QuotaAccountDao;
import org.apache.cloudstack.quota.dao.QuotaBalanceDao; import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
import org.apache.cloudstack.quota.dao.QuotaCreditsDao; 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.QuotaEmailTemplatesDao;
import org.apache.cloudstack.quota.dao.QuotaTariffDao; 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.QuotaAccountVO;
import org.apache.cloudstack.quota.dao.QuotaUsageDao;
import org.apache.cloudstack.quota.vo.QuotaBalanceVO; import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
import org.apache.cloudstack.quota.vo.QuotaCreditsVO; 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.QuotaEmailTemplatesVO;
import org.apache.cloudstack.quota.vo.QuotaTariffVO; import org.apache.cloudstack.quota.vo.QuotaTariffVO;
import org.apache.cloudstack.quota.vo.QuotaUsageVO; import org.apache.cloudstack.quota.vo.QuotaUsageVO;
@ -104,7 +107,7 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
@Inject @Inject
private AccountDao _accountDao; private AccountDao _accountDao;
@Inject @Inject
private QuotaAccountDao _quotaAccountDao; private QuotaAccountDao quotaAccountDao;
@Inject @Inject
private DomainDao _domainDao; private DomainDao _domainDao;
@Inject @Inject
@ -113,6 +116,8 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
private QuotaStatement _statement; private QuotaStatement _statement;
@Inject @Inject
private QuotaManager _quotaManager; private QuotaManager _quotaManager;
@Inject
private QuotaEmailConfigurationDao quotaEmailConfigurationDao;
@Override @Override
public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) { public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff) {
@ -165,7 +170,7 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
result.add(qr); result.add(qr);
} }
} else { } else {
Pair<List<QuotaAccountVO>, Integer> data = _quotaAccountDao.listAllQuotaAccount(startIndex, pageSize); Pair<List<QuotaAccountVO>, Integer> data = quotaAccountDao.listAllQuotaAccount(startIndex, pageSize);
count = data.second(); count = data.second();
for (final QuotaAccountVO quotaAccount : data.first()) { for (final QuotaAccountVO quotaAccount : data.first()) {
AccountVO account = _accountDao.findById(quotaAccount.getId()); AccountVO account = _accountDao.findById(quotaAccount.getId());
@ -676,4 +681,99 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
return _quotaTariffDao.updateQuotaTariff(quotaTariff); 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;
}
} }

View File

@ -28,10 +28,12 @@ import javax.inject.Inject;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import org.apache.cloudstack.api.command.QuotaBalanceCmd; 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.QuotaCreditsCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd; import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd; import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
import org.apache.cloudstack.api.command.QuotaEnabledCmd; 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.QuotaStatementCmd;
import org.apache.cloudstack.api.command.QuotaSummaryCmd; import org.apache.cloudstack.api.command.QuotaSummaryCmd;
import org.apache.cloudstack.api.command.QuotaTariffCreateCmd; 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(QuotaEmailTemplateUpdateCmd.class);
cmdList.add(QuotaTariffCreateCmd.class); cmdList.add(QuotaTariffCreateCmd.class);
cmdList.add(QuotaTariffDeleteCmd.class); cmdList.add(QuotaTariffDeleteCmd.class);
cmdList.add(QuotaConfigureEmailCmd.class);
cmdList.add(QuotaListEmailConfigurationCmd.class);
return cmdList; return cmdList;
} }
@ -128,7 +132,7 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
public ConfigKey<?>[] getConfigKeys() { public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaCurrencyLocale, QuotaStatementPeriod, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout, return new ConfigKey<?>[] {QuotaPluginEnabled, QuotaEnableEnforcement, QuotaCurrencySymbol, QuotaCurrencyLocale, QuotaStatementPeriod, QuotaSmtpHost, QuotaSmtpPort, QuotaSmtpTimeout,
QuotaSmtpUser, QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender, QuotaSmtpEnabledSecurityProtocols, QuotaSmtpUseStartTLS, QuotaActivationRuleTimeout, QuotaAccountEnabled, QuotaSmtpUser, QuotaSmtpPassword, QuotaSmtpAuthType, QuotaSmtpSender, QuotaSmtpEnabledSecurityProtocols, QuotaSmtpUseStartTLS, QuotaActivationRuleTimeout, QuotaAccountEnabled,
QuotaEmailHeader, QuotaEmailFooter}; QuotaEmailHeader, QuotaEmailFooter, QuotaEnableEmails};
} }
@Override @Override

View File

@ -30,6 +30,7 @@ import java.util.function.Consumer;
import com.cloud.domain.DomainVO; import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao; import com.cloud.domain.dao.DomainDao;
import org.apache.cloudstack.api.ServerApiException; 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.QuotaEmailTemplateListCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd; import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
import org.apache.cloudstack.framework.config.ConfigKey; 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.QuotaStatement;
import org.apache.cloudstack.quota.constant.QuotaConfig; import org.apache.cloudstack.quota.constant.QuotaConfig;
import org.apache.cloudstack.quota.constant.QuotaTypes; 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.QuotaBalanceDao;
import org.apache.cloudstack.quota.dao.QuotaCreditsDao; 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.QuotaEmailTemplatesDao;
import org.apache.cloudstack.quota.dao.QuotaTariffDao; import org.apache.cloudstack.quota.dao.QuotaTariffDao;
import org.apache.cloudstack.quota.dao.QuotaUsageDao; 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.QuotaBalanceVO;
import org.apache.cloudstack.quota.vo.QuotaCreditsVO; 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.QuotaEmailTemplatesVO;
import org.apache.cloudstack.quota.vo.QuotaTariffVO; import org.apache.cloudstack.quota.vo.QuotaTariffVO;
import org.apache.commons.lang3.time.DateUtils; import org.apache.commons.lang3.time.DateUtils;
@ -103,6 +108,12 @@ public class QuotaResponseBuilderImplTest extends TestCase {
@Mock @Mock
QuotaUsageDao quotaUsageDaoMock; QuotaUsageDao quotaUsageDaoMock;
@Mock
QuotaAccountDao quotaAccountDaoMock;
@Mock
QuotaEmailConfigurationDao quotaEmailConfigurationDaoMock;
@InjectMocks @InjectMocks
QuotaResponseBuilderImpl quotaResponseBuilderSpy = Mockito.spy(QuotaResponseBuilderImpl.class); QuotaResponseBuilderImpl quotaResponseBuilderSpy = Mockito.spy(QuotaResponseBuilderImpl.class);
@ -114,6 +125,15 @@ public class QuotaResponseBuilderImplTest extends TestCase {
@Mock @Mock
DomainVO domainVOMock; DomainVO domainVOMock;
@Mock
QuotaConfigureEmailCmd quotaConfigureEmailCmdMock;
@Mock
QuotaAccountVO quotaAccountVOMock;
@Mock
QuotaEmailTemplatesVO quotaEmailTemplatesVoMock;
private void overrideDefaultQuotaEnabledConfigValue(final Object value) throws IllegalAccessException, NoSuchFieldException { private void overrideDefaultQuotaEnabledConfigValue(final Object value) throws IllegalAccessException, NoSuchFieldException {
Field f = ConfigKey.class.getDeclaredField("_defaultValue"); Field f = ConfigKey.class.getDeclaredField("_defaultValue");
f.setAccessible(true); f.setAccessible(true);
@ -403,4 +423,96 @@ public class QuotaResponseBuilderImplTest extends TestCase {
assertTrue(quotaSummaryResponse.getQuotaEnabled()); 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());
}
} }