Skip to content

Commit

Permalink
Quota tariff order (apache#8347)
Browse files Browse the repository at this point in the history
  • Loading branch information
JoaoJandre authored and dhslove committed Jul 23, 2024
1 parent 9d1d752 commit 05c6eba
Show file tree
Hide file tree
Showing 11 changed files with 351 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,7 @@ SET
WHERE
name IN ("quota.usage.smtp.useStartTLS", "quota.usage.smtp.useAuth", "alert.smtp.useAuth", "project.smtp.useAuth")
AND value NOT IN ("true", "y", "t", "1", "on", "yes");


-- Quota inject tariff result into subsequent ones
CALL `cloud_usage`.`IDEMPOTENT_ADD_COLUMN`('cloud_usage.quota_tariff', 'position', 'bigint(20) NOT NULL DEFAULT 1 COMMENT "Position in the execution sequence for tariffs of the same type"');
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
Expand All @@ -36,6 +37,7 @@
import org.apache.cloudstack.quota.activationrule.presetvariables.GenericPresetVariable;
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableHelper;
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariables;
import org.apache.cloudstack.quota.activationrule.presetvariables.Tariff;
import org.apache.cloudstack.quota.constant.QuotaConfig;
import org.apache.cloudstack.quota.constant.QuotaTypes;
import org.apache.cloudstack.quota.dao.QuotaAccountDao;
Expand Down Expand Up @@ -371,9 +373,22 @@ protected BigDecimal aggregateQuotaTariffsValues(UsageVO usageRecord, List<Quota
PresetVariables presetVariables = getPresetVariables(hasAnyQuotaTariffWithActivationRule, usageRecord);
BigDecimal aggregatedQuotaTariffsValue = BigDecimal.ZERO;

quotaTariffs.sort(Comparator.comparing(QuotaTariffVO::getPosition));

List<Tariff> lastTariffs = new ArrayList<>();


for (QuotaTariffVO quotaTariff : quotaTariffs) {
if (isQuotaTariffInPeriodToBeApplied(usageRecord, quotaTariff, accountToString)) {
aggregatedQuotaTariffsValue = aggregatedQuotaTariffsValue.add(getQuotaTariffValueToBeApplied(quotaTariff, jsInterpreter, presetVariables));

BigDecimal tariffValue = getQuotaTariffValueToBeApplied(quotaTariff, jsInterpreter, presetVariables, lastTariffs);

aggregatedQuotaTariffsValue = aggregatedQuotaTariffsValue.add(tariffValue);

Tariff tariffPresetVariable = new Tariff();
tariffPresetVariable.setId(quotaTariff.getUuid());
tariffPresetVariable.setValue(tariffValue);
lastTariffs.add(tariffPresetVariable);
}
}

Expand Down Expand Up @@ -401,7 +416,7 @@ protected PresetVariables getPresetVariables(boolean hasAnyQuotaTariffWithActiva
* <li>If the activation rule result in something else, returns {@link BigDecimal#ZERO}.</li>
* </ul>
*/
protected BigDecimal getQuotaTariffValueToBeApplied(QuotaTariffVO quotaTariff, JsInterpreter jsInterpreter, PresetVariables presetVariables) {
protected BigDecimal getQuotaTariffValueToBeApplied(QuotaTariffVO quotaTariff, JsInterpreter jsInterpreter, PresetVariables presetVariables, List<Tariff> lastAppliedTariffsList) {
String activationRule = quotaTariff.getActivationRule();
BigDecimal quotaTariffValue = quotaTariff.getCurrencyValue();
String quotaTariffToString = quotaTariff.toString(usageAggregationTimeZone);
Expand All @@ -413,6 +428,7 @@ protected BigDecimal getQuotaTariffValueToBeApplied(QuotaTariffVO quotaTariff, J
}

injectPresetVariablesIntoJsInterpreter(jsInterpreter, presetVariables);
jsInterpreter.injectVariable("lastTariffs", lastAppliedTariffsList.toString());

String scriptResult = jsInterpreter.executeScript(activationRule).toString();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// 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.activationrule.presetvariables;

import java.math.BigDecimal;

public class Tariff extends GenericPresetVariable {
private BigDecimal value;

public BigDecimal getValue() {
return value;
}

public void setValue(BigDecimal value) {
this.value = value;
fieldNamesToIncludeInToString.add("value");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ public class QuotaTariffVO implements QuotaTariff {
@Temporal(value = TemporalType.TIMESTAMP)
private Date endDate;

@Column(name = "position")
protected Integer position;


public QuotaTariffVO() {
}

Expand Down Expand Up @@ -120,6 +124,7 @@ public QuotaTariffVO(QuotaTariffVO that) {
this.setDescription(that.getDescription());
this.setActivationRule(that.getActivationRule());
this.setEndDate(that.getEndDate());
this.setPosition(that.getPosition());
}

public void setId(Long id) {
Expand Down Expand Up @@ -263,6 +268,15 @@ public boolean setUsageTypeData(int usageType) {
return true;
}

public Integer getPosition() {
return position;
}

public void setPosition(Integer position) {
this.position = position;
}


@Override
public String toString() {
return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "uuid", "name", "usageName");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.apache.cloudstack.quota.activationrule.presetvariables.GenericPresetVariable;
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableHelper;
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariables;
import org.apache.cloudstack.quota.activationrule.presetvariables.Tariff;
import org.apache.cloudstack.quota.activationrule.presetvariables.Value;
import org.apache.cloudstack.quota.constant.QuotaTypes;
import org.apache.cloudstack.quota.dao.QuotaTariffDao;
Expand Down Expand Up @@ -395,7 +396,7 @@ public void getQuotaTariffValueToBeAppliedTestActivationRuleIsNullReturnTariffVa
Mockito.doReturn(null).when(quotaTariffVoMock).getActivationRule();
Mockito.doReturn(BigDecimal.ONE).when(quotaTariffVoMock).getCurrencyValue();

BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, null, null);
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, null, null, null);

Assert.assertEquals(BigDecimal.ONE, result);
}
Expand All @@ -405,59 +406,66 @@ public void getQuotaTariffValueToBeAppliedTestActivationRuleIsEmptyReturnTariffV
Mockito.doReturn("").when(quotaTariffVoMock).getActivationRule();
Mockito.doReturn(BigDecimal.TEN).when(quotaTariffVoMock).getCurrencyValue();

BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, null, null);
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, null, null, null);

Assert.assertEquals(BigDecimal.TEN, result);
}

@Test
public void getQuotaTariffValueToBeAppliedTestScriptResultIsNumberReturnIt() {
BigDecimal expected = new BigDecimal(50.1);
List<Tariff> lastTariffs = createLastAppliedTariffsPresetVariableList(0);


Mockito.doReturn(" ").when(quotaTariffVoMock).getActivationRule();
Mockito.doReturn(BigDecimal.TEN).when(quotaTariffVoMock).getCurrencyValue();
Mockito.doNothing().when(quotaManagerImplSpy).injectPresetVariablesIntoJsInterpreter(Mockito.any(), Mockito.any());
Mockito.doReturn(expected).when(jsInterpreterMock).executeScript(Mockito.anyString());

BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock);
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock, lastTariffs);

Assert.assertEquals(expected, result);
}

@Test
public void getQuotaTariffValueToBeAppliedTestScriptResultIsTrueReturnTariffValue() {
BigDecimal expected = new BigDecimal(236.84);
List<Tariff> lastTariffs = createLastAppliedTariffsPresetVariableList(0);

Mockito.doReturn(" ").when(quotaTariffVoMock).getActivationRule();
Mockito.doReturn(expected).when(quotaTariffVoMock).getCurrencyValue();
Mockito.doNothing().when(quotaManagerImplSpy).injectPresetVariablesIntoJsInterpreter(Mockito.any(), Mockito.any());
Mockito.doReturn(true).when(jsInterpreterMock).executeScript(Mockito.anyString());

BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock);
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock, lastTariffs);

Assert.assertEquals(expected, result);
}

@Test
public void getQuotaTariffValueToBeAppliedTestScriptResultIsFalseReturnZero() {
List<Tariff> lastTariffs = createLastAppliedTariffsPresetVariableList(0);

Mockito.doReturn(" ").when(quotaTariffVoMock).getActivationRule();
Mockito.doReturn(BigDecimal.TEN).when(quotaTariffVoMock).getCurrencyValue();
Mockito.doNothing().when(quotaManagerImplSpy).injectPresetVariablesIntoJsInterpreter(Mockito.any(), Mockito.any());
Mockito.doReturn(false).when(jsInterpreterMock).executeScript(Mockito.anyString());

BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock);
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock, lastTariffs);

Assert.assertEquals(BigDecimal.ZERO, result);
}

@Test
public void getQuotaTariffValueToBeAppliedTestScriptResultIsNotBooleanNorNumericReturnZero() {
List<Tariff> lastTariffs = createLastAppliedTariffsPresetVariableList(0);

Mockito.doReturn(" ").when(quotaTariffVoMock).getActivationRule();
Mockito.doReturn(BigDecimal.TEN).when(quotaTariffVoMock).getCurrencyValue();
Mockito.doNothing().when(quotaManagerImplSpy).injectPresetVariablesIntoJsInterpreter(Mockito.any(), Mockito.any());
Mockito.doReturn("test").when(jsInterpreterMock).executeScript(Mockito.anyString());

BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock);
BigDecimal result = quotaManagerImplSpy.getQuotaTariffValueToBeApplied(quotaTariffVoMock, jsInterpreterMock, presetVariablesMock, lastTariffs);

Assert.assertEquals(BigDecimal.ZERO, result);
}
Expand All @@ -477,10 +485,7 @@ public void getPresetVariablesTestHasTariffsWithActivationRuleReturnPresetVariab

@Test
public void aggregateQuotaTariffsValuesTestTariffsWereNotInPeriodToBeAppliedReturnZero() {
List<QuotaTariffVO> tariffs = new ArrayList<>();
tariffs.add(new QuotaTariffVO());
tariffs.add(new QuotaTariffVO());
tariffs.add(new QuotaTariffVO());
List<QuotaTariffVO> tariffs = createTariffList();

Mockito.doReturn(false).when(quotaManagerImplSpy).isQuotaTariffInPeriodToBeApplied(Mockito.any(), Mockito.any(), Mockito.anyString());
BigDecimal result = quotaManagerImplSpy.aggregateQuotaTariffsValues(usageVoMock, tariffs, false, jsInterpreterMock, "");
Expand All @@ -497,13 +502,10 @@ public void aggregateQuotaTariffsValuesTestTariffsIsEmptyReturnZero() {

@Test
public void aggregateQuotaTariffsValuesTestTariffsAreInPeriodToBeAppliedReturnAggregation() {
List<QuotaTariffVO> tariffs = new ArrayList<>();
tariffs.add(new QuotaTariffVO());
tariffs.add(new QuotaTariffVO());
tariffs.add(new QuotaTariffVO());
List<QuotaTariffVO> tariffs = createTariffList();

Mockito.doReturn(true, false, true).when(quotaManagerImplSpy).isQuotaTariffInPeriodToBeApplied(Mockito.any(), Mockito.any(), Mockito.anyString());
Mockito.doReturn(BigDecimal.TEN).when(quotaManagerImplSpy).getQuotaTariffValueToBeApplied(Mockito.any(), Mockito.any(), Mockito.any());
Mockito.doReturn(BigDecimal.TEN).when(quotaManagerImplSpy).getQuotaTariffValueToBeApplied(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
BigDecimal result = quotaManagerImplSpy.aggregateQuotaTariffsValues(usageVoMock, tariffs, false, jsInterpreterMock, "");

Assert.assertEquals(BigDecimal.TEN.multiply(new BigDecimal(2)), result);
Expand All @@ -528,4 +530,25 @@ public void persistUsagesAndQuotaUsagesAndRetrievePersistedQuotaUsagesTestReturn
Assert.assertEquals(quotaUsageVoMock1, result.get(0));
Assert.assertEquals(quotaUsageVoMock2, result.get(1));
}

private static List<QuotaTariffVO> createTariffList() {
List<QuotaTariffVO> tariffs = new ArrayList<>();
tariffs.add(new QuotaTariffVO());
tariffs.add(new QuotaTariffVO());
tariffs.add(new QuotaTariffVO());
tariffs.forEach(quotaTariffVO -> quotaTariffVO.setPosition(1));
return tariffs;
}

private static List<Tariff> createLastAppliedTariffsPresetVariableList(int numberOfTariffs) {
List<Tariff> lastTariffs = new ArrayList<>();
for (int i = 0; i < numberOfTariffs; i++) {
Tariff tariff = new Tariff();
tariff.setId(String.valueOf(i));
tariff.setValue(BigDecimal.valueOf(i));
lastTariffs.add(tariff);
}
return lastTariffs;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ public class QuotaTariffCreateCmd extends BaseCmd {
ApiConstants.PARAMETER_DESCRIPTION_END_DATE_POSSIBLE_FORMATS)
private Date endDate;

@Parameter(name = ApiConstants.POSITION, type = CommandType.INTEGER, description = "Position in the execution sequence for tariffs of the same type", since = "4.20.0.0")
private Integer position;

@Override
public void execute() {
CallContext.current().setEventDetails(String.format("Tariff: %s, description: %s, value: %s", getName(), getDescription(), getValue()));
Expand Down Expand Up @@ -139,4 +142,13 @@ public void setEndDate(Date endDate) {
public ApiCommandResourceType getApiResourceType() {
return ApiCommandResourceType.QuotaTariff;
}
public Integer getPosition() {
return position;
}

public void setPosition(Integer position) {
this.position = position;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ public class QuotaTariffUpdateCmd extends BaseCmd {
"value will be applied. Inform empty to remove the activation rule.", length = 65535, since = "4.18.0.0")
private String activationRule;

@Parameter(name = ApiConstants.POSITION, type = CommandType.INTEGER, description = "Position in the execution sequence for tariffs of the same type", since = "4.20.0.0")
private Integer position;

public Integer getUsageType() {
return usageType;
}
Expand Down Expand Up @@ -130,4 +133,13 @@ public long getEntityOwnerId() {
public ApiCommandResourceType getApiResourceType() {
return ApiCommandResourceType.QuotaTariff;
}

public Integer getPosition() {
return position;
}

public void setPosition(Integer position) {
this.position = position;
}

}
Loading

0 comments on commit 05c6eba

Please sign in to comment.