mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
API to validate Quota activation rule (#9605)
* API to validate Quota activation rule * Apply suggestions from code review Co-authored-by: Bryan Lima <42067040+BryanMLima@users.noreply.github.com> * Use constants --------- Co-authored-by: Henrique Sato <henrique.sato@scclouds.com.br> Co-authored-by: Bryan Lima <42067040+BryanMLima@users.noreply.github.com>
This commit is contained in:
parent
9b6f9b5f7d
commit
4ac4d9cf29
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
package org.apache.cloudstack.quota.activationrule.presetvariables;
|
package org.apache.cloudstack.quota.activationrule.presetvariables;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class PresetVariables {
|
public class PresetVariables {
|
||||||
|
|
||||||
@PresetVariableDefinition(description = "Account owner of the resource.")
|
@PresetVariableDefinition(description = "Account owner of the resource.")
|
||||||
@ -37,6 +39,9 @@ public class PresetVariables {
|
|||||||
@PresetVariableDefinition(description = "Zone where the resource is.")
|
@PresetVariableDefinition(description = "Zone where the resource is.")
|
||||||
private GenericPresetVariable zone;
|
private GenericPresetVariable zone;
|
||||||
|
|
||||||
|
@PresetVariableDefinition(description = "A list containing the tariffs ordered by the field 'position'.")
|
||||||
|
private List<Tariff> lastTariffs;
|
||||||
|
|
||||||
public Account getAccount() {
|
public Account getAccount() {
|
||||||
return account;
|
return account;
|
||||||
}
|
}
|
||||||
@ -84,4 +89,12 @@ public class PresetVariables {
|
|||||||
public void setZone(GenericPresetVariable zone) {
|
public void setZone(GenericPresetVariable zone) {
|
||||||
this.zone = zone;
|
this.zone = zone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Tariff> getLastTariffs() {
|
||||||
|
return lastTariffs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastTariffs(List<Tariff> lastTariffs) {
|
||||||
|
this.lastTariffs = lastTariffs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,9 +20,11 @@ import java.util.Collections;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
import org.apache.cloudstack.usage.UsageTypes;
|
import org.apache.cloudstack.usage.UsageTypes;
|
||||||
import org.apache.cloudstack.usage.UsageUnitTypes;
|
import org.apache.cloudstack.usage.UsageUnitTypes;
|
||||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
public class QuotaTypes extends UsageTypes {
|
public class QuotaTypes extends UsageTypes {
|
||||||
private final Integer quotaType;
|
private final Integer quotaType;
|
||||||
@ -106,6 +108,20 @@ public class QuotaTypes extends UsageTypes {
|
|||||||
return quotaTypeMap.get(quotaType);
|
return quotaTypeMap.get(quotaType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static public QuotaTypes getQuotaTypeByName(String name) {
|
||||||
|
if (StringUtils.isBlank(name)) {
|
||||||
|
throw new CloudRuntimeException("Could not retrieve Quota type by name because the value passed as parameter is null, empty, or blank.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (QuotaTypes type : quotaTypeMap.values()) {
|
||||||
|
if (type.getQuotaName().equals(name)) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new CloudRuntimeException(String.format("Could not find Quota type with name [%s].", name));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "quotaType", "quotaName");
|
return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "quotaType", "quotaName");
|
||||||
|
|||||||
@ -0,0 +1,70 @@
|
|||||||
|
//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.exception.InvalidParameterValueException;
|
||||||
|
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.QuotaResponseBuilder;
|
||||||
|
import org.apache.cloudstack.api.response.QuotaValidateActivationRuleResponse;
|
||||||
|
import org.apache.cloudstack.quota.constant.QuotaTypes;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
@APICommand(name = "quotaValidateActivationRule", responseObject = QuotaValidateActivationRuleResponse.class, description = "Validates if the given activation rule is valid for the informed usage type.", since = "4.20.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||||
|
public class QuotaValidateActivationRuleCmd extends BaseCmd {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
QuotaResponseBuilder responseBuilder;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.ACTIVATION_RULE, type = CommandType.STRING, required = true, description = "Quota tariff's activation rule to validate. The activation rule is valid if it has no syntax errors and all " +
|
||||||
|
"variables are compatible with the given usage type.", length = 65535)
|
||||||
|
private String activationRule;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.USAGE_TYPE, type = CommandType.INTEGER, required = true, description = "The Quota usage type used to validate the activation rule.")
|
||||||
|
private Integer quotaType;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
QuotaValidateActivationRuleResponse response = responseBuilder.validateActivationRule(this);
|
||||||
|
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
return Account.ACCOUNT_ID_SYSTEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getActivationRule() {
|
||||||
|
return activationRule;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QuotaTypes getQuotaType() {
|
||||||
|
QuotaTypes quotaTypes = QuotaTypes.getQuotaType(quotaType);
|
||||||
|
|
||||||
|
if (quotaTypes == null) {
|
||||||
|
throw new InvalidParameterValueException(String.format("Usage type not found for value [%s].", quotaType));
|
||||||
|
}
|
||||||
|
|
||||||
|
return quotaTypes;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -26,6 +26,7 @@ import org.apache.cloudstack.api.command.QuotaStatementCmd;
|
|||||||
import org.apache.cloudstack.api.command.QuotaTariffCreateCmd;
|
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.api.command.QuotaValidateActivationRuleCmd;
|
||||||
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.QuotaEmailConfigurationVO;
|
||||||
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
|
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
|
||||||
@ -88,4 +89,6 @@ public interface QuotaResponseBuilder {
|
|||||||
QuotaConfigureEmailResponse createQuotaConfigureEmailResponse(QuotaEmailConfigurationVO quotaEmailConfigurationVO, Double minBalance, long accountId);
|
QuotaConfigureEmailResponse createQuotaConfigureEmailResponse(QuotaEmailConfigurationVO quotaEmailConfigurationVO, Double minBalance, long accountId);
|
||||||
|
|
||||||
List<QuotaConfigureEmailResponse> listEmailConfiguration(long accountId);
|
List<QuotaConfigureEmailResponse> listEmailConfiguration(long accountId);
|
||||||
|
|
||||||
|
QuotaValidateActivationRuleResponse validateActivationRule(QuotaValidateActivationRuleCmd cmd);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
//under the License.
|
//under the License.
|
||||||
package org.apache.cloudstack.api.response;
|
package org.apache.cloudstack.api.response;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
@ -34,12 +35,15 @@ import java.util.HashMap;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import com.cloud.utils.DateUtil;
|
import com.cloud.utils.DateUtil;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
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;
|
||||||
@ -51,8 +55,10 @@ import org.apache.cloudstack.api.command.QuotaStatementCmd;
|
|||||||
import org.apache.cloudstack.api.command.QuotaTariffCreateCmd;
|
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.api.command.QuotaValidateActivationRuleCmd;
|
||||||
import org.apache.cloudstack.context.CallContext;
|
import org.apache.cloudstack.context.CallContext;
|
||||||
import org.apache.cloudstack.discovery.ApiDiscoveryService;
|
import org.apache.cloudstack.discovery.ApiDiscoveryService;
|
||||||
|
import org.apache.cloudstack.jsinterpreter.JsInterpreterHelper;
|
||||||
import org.apache.cloudstack.quota.QuotaManager;
|
import org.apache.cloudstack.quota.QuotaManager;
|
||||||
import org.apache.cloudstack.quota.QuotaManagerImpl;
|
import org.apache.cloudstack.quota.QuotaManagerImpl;
|
||||||
import org.apache.cloudstack.quota.QuotaService;
|
import org.apache.cloudstack.quota.QuotaService;
|
||||||
@ -78,6 +84,7 @@ 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;
|
||||||
|
import org.apache.cloudstack.utils.jsinterpreter.JsInterpreter;
|
||||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||||
@ -133,11 +140,13 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
|
|||||||
private QuotaManager _quotaManager;
|
private QuotaManager _quotaManager;
|
||||||
@Inject
|
@Inject
|
||||||
private QuotaEmailConfigurationDao quotaEmailConfigurationDao;
|
private QuotaEmailConfigurationDao quotaEmailConfigurationDao;
|
||||||
|
@Inject
|
||||||
|
private JsInterpreterHelper jsInterpreterHelper;
|
||||||
|
@Inject
|
||||||
|
private ApiDiscoveryService apiDiscoveryService;
|
||||||
|
|
||||||
private final Class<?>[] assignableClasses = {GenericPresetVariable.class, ComputingResources.class};
|
private final Class<?>[] assignableClasses = {GenericPresetVariable.class, ComputingResources.class};
|
||||||
|
|
||||||
@Inject
|
|
||||||
private ApiDiscoveryService apiDiscoveryService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff, boolean returnActivationRule) {
|
public QuotaTariffResponse createQuotaTariffResponse(QuotaTariffVO tariff, boolean returnActivationRule) {
|
||||||
@ -789,7 +798,7 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
|
|||||||
*/
|
*/
|
||||||
public void filterSupportedTypes(List<Pair<String, String>> variables, QuotaTypes quotaType, PresetVariableDefinition presetVariableDefinitionAnnotation, Class<?> fieldClass,
|
public void filterSupportedTypes(List<Pair<String, String>> variables, QuotaTypes quotaType, PresetVariableDefinition presetVariableDefinitionAnnotation, Class<?> fieldClass,
|
||||||
String presetVariableName) {
|
String presetVariableName) {
|
||||||
if (Arrays.stream(presetVariableDefinitionAnnotation.supportedTypes()).noneMatch(supportedType ->
|
if (quotaType != null && Arrays.stream(presetVariableDefinitionAnnotation.supportedTypes()).noneMatch(supportedType ->
|
||||||
supportedType == quotaType.getQuotaType() || supportedType == 0)) {
|
supportedType == quotaType.getQuotaType() || supportedType == 0)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -928,4 +937,82 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
|
|||||||
|
|
||||||
return quotaConfigureEmailResponse;
|
return quotaConfigureEmailResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QuotaValidateActivationRuleResponse validateActivationRule(QuotaValidateActivationRuleCmd cmd) {
|
||||||
|
String message;
|
||||||
|
String activationRule = cmd.getActivationRule();
|
||||||
|
QuotaTypes quotaType = cmd.getQuotaType();
|
||||||
|
String quotaName = quotaType.getQuotaName();
|
||||||
|
List<Pair<String, String>> usageTypeVariablesAndDescriptions = new ArrayList<>();
|
||||||
|
|
||||||
|
addAllPresetVariables(PresetVariables.class, quotaType, usageTypeVariablesAndDescriptions, null);
|
||||||
|
List<String> usageTypeVariables = usageTypeVariablesAndDescriptions.stream().map(Pair::first).collect(Collectors.toList());
|
||||||
|
|
||||||
|
try (JsInterpreter jsInterpreter = new JsInterpreter(QuotaConfig.QuotaActivationRuleTimeout.value())) {
|
||||||
|
Map<String, String> newVariables = injectUsageTypeVariables(jsInterpreter, usageTypeVariables);
|
||||||
|
String scriptToExecute = jsInterpreterHelper.replaceScriptVariables(activationRule, newVariables);
|
||||||
|
jsInterpreter.executeScript(String.format("new Function(\"%s\")", scriptToExecute.replaceAll("\n", "")));
|
||||||
|
} catch (IOException | CloudRuntimeException e) {
|
||||||
|
logger.error("Unable to execute activation rule due to: [{}].", e.getMessage(), e);
|
||||||
|
message = "Error while executing activation rule. Check if there are no syntax errors and all variables are compatible with the given usage type.";
|
||||||
|
return createValidateActivationRuleResponse(activationRule, quotaName, false, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> scriptVariables = jsInterpreterHelper.getScriptVariables(activationRule);
|
||||||
|
if (isScriptVariablesValid(scriptVariables, usageTypeVariables)) {
|
||||||
|
message = "The script has no syntax errors and all variables are compatible with the given usage type.";
|
||||||
|
return createValidateActivationRuleResponse(activationRule, quotaName, true, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
message = "Found variables that are not compatible with the given usage type.";
|
||||||
|
return createValidateActivationRuleResponse(activationRule, quotaName, false, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether script variables are compatible with the usage type. First, we remove all script variables that correspond to the script's usage type variables.
|
||||||
|
* Then, returns true if none of the remaining script variables match any usage types variables, and false otherwise.
|
||||||
|
*
|
||||||
|
* @param scriptVariables Script variables.
|
||||||
|
* @param scriptUsageTypeVariables Script usage type variables.
|
||||||
|
* @return True if the script variables are valid, false otherwise.
|
||||||
|
*/
|
||||||
|
protected boolean isScriptVariablesValid(Set<String> scriptVariables, List<String> scriptUsageTypeVariables) {
|
||||||
|
List<Pair<String, String>> allUsageTypeVariablesAndDescriptions = new ArrayList<>();
|
||||||
|
addAllPresetVariables(PresetVariables.class, null, allUsageTypeVariablesAndDescriptions, null);
|
||||||
|
List<String> allUsageTypesVariables = allUsageTypeVariablesAndDescriptions.stream().map(Pair::first).collect(Collectors.toList());
|
||||||
|
|
||||||
|
List<String> matchVariables = scriptVariables.stream().filter(scriptUsageTypeVariables::contains).collect(Collectors.toList());
|
||||||
|
matchVariables.forEach(scriptVariables::remove);
|
||||||
|
|
||||||
|
return scriptVariables.stream().noneMatch(allUsageTypesVariables::contains);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injects variables into JavaScript interpreter. It's necessary to remove all dots from the given variables, as the interpreter
|
||||||
|
* does not interpret the variables as attributes of objects.
|
||||||
|
*
|
||||||
|
* @param jsInterpreter the {@link JsInterpreter} which the variables will be injected.
|
||||||
|
* @param variables the {@link List} with variables to format and inject the formatted variables into interpreter.
|
||||||
|
* @return A {@link Map} which has the key as the given variable and the value as the given variable formatted (without dots).
|
||||||
|
*/
|
||||||
|
protected Map<String, String> injectUsageTypeVariables(JsInterpreter jsInterpreter, List<String> variables) {
|
||||||
|
Map<String, String> formattedVariables = new HashMap<>();
|
||||||
|
for (String variable : variables) {
|
||||||
|
String formattedVariable = variable.replace(".", "");
|
||||||
|
formattedVariables.put(variable, formattedVariable);
|
||||||
|
jsInterpreter.injectVariable(formattedVariable, "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
return formattedVariables;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QuotaValidateActivationRuleResponse createValidateActivationRuleResponse(String activationRule, String quotaType, Boolean isValid, String message) {
|
||||||
|
QuotaValidateActivationRuleResponse response = new QuotaValidateActivationRuleResponse();
|
||||||
|
response.setActivationRule(activationRule);
|
||||||
|
response.setQuotaType(quotaType);
|
||||||
|
response.setValid(isValid);
|
||||||
|
response.setMessage(message);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,76 @@
|
|||||||
|
//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 QuotaValidateActivationRuleResponse extends BaseResponse {
|
||||||
|
|
||||||
|
@SerializedName("activationrule")
|
||||||
|
@Param(description = "The validated activation rule.")
|
||||||
|
private String activationRule;
|
||||||
|
|
||||||
|
@SerializedName("quotatype")
|
||||||
|
@Param(description = "The Quota usage type used to validate the activation rule.")
|
||||||
|
private String quotaType;
|
||||||
|
|
||||||
|
@SerializedName("isvalid")
|
||||||
|
@Param(description = "Whether the activation rule is valid.")
|
||||||
|
private Boolean isValid;
|
||||||
|
|
||||||
|
@SerializedName("message")
|
||||||
|
@Param(description = "The reason whether the activation rule is valid or not.")
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public QuotaValidateActivationRuleResponse() {
|
||||||
|
super("validactivationrule");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getActivationRule() {
|
||||||
|
return activationRule;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActivationRule(String activationRule) {
|
||||||
|
this.activationRule = activationRule;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean isValid() {
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValid(Boolean valid) {
|
||||||
|
isValid = valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getQuotaType() {
|
||||||
|
return quotaType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQuotaType(String quotaType) {
|
||||||
|
this.quotaType = quotaType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -41,6 +41,7 @@ import org.apache.cloudstack.api.command.QuotaTariffDeleteCmd;
|
|||||||
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.api.command.QuotaUpdateCmd;
|
import org.apache.cloudstack.api.command.QuotaUpdateCmd;
|
||||||
|
import org.apache.cloudstack.api.command.QuotaValidateActivationRuleCmd;
|
||||||
import org.apache.cloudstack.api.response.QuotaResponseBuilder;
|
import org.apache.cloudstack.api.response.QuotaResponseBuilder;
|
||||||
import org.apache.cloudstack.context.CallContext;
|
import org.apache.cloudstack.context.CallContext;
|
||||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||||
@ -121,6 +122,7 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
|
|||||||
cmdList.add(QuotaConfigureEmailCmd.class);
|
cmdList.add(QuotaConfigureEmailCmd.class);
|
||||||
cmdList.add(QuotaListEmailConfigurationCmd.class);
|
cmdList.add(QuotaListEmailConfigurationCmd.class);
|
||||||
cmdList.add(QuotaPresetVariablesListCmd.class);
|
cmdList.add(QuotaPresetVariablesListCmd.class);
|
||||||
|
cmdList.add(QuotaValidateActivationRuleCmd.class);
|
||||||
return cmdList;
|
return cmdList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,41 @@
|
|||||||
|
// 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 org.apache.cloudstack.api.response.QuotaResponseBuilder;
|
||||||
|
import org.apache.cloudstack.api.response.QuotaValidateActivationRuleResponse;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class QuotaValidateActivationRuleCmdTest {
|
||||||
|
@Mock
|
||||||
|
QuotaResponseBuilder responseBuilderMock;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void executeTestVerifyCalls() {
|
||||||
|
QuotaValidateActivationRuleCmd cmd = new QuotaValidateActivationRuleCmd();
|
||||||
|
cmd.responseBuilder = responseBuilderMock;
|
||||||
|
Mockito.doReturn(new QuotaValidateActivationRuleResponse()).when(responseBuilderMock).validateActivationRule(cmd);
|
||||||
|
cmd.execute();
|
||||||
|
|
||||||
|
Mockito.verify(responseBuilderMock).validateActivationRule(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -25,6 +25,9 @@ import java.util.ArrayList;
|
|||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import com.cloud.domain.DomainVO;
|
import com.cloud.domain.DomainVO;
|
||||||
@ -34,7 +37,10 @@ import org.apache.cloudstack.api.ServerApiException;
|
|||||||
import org.apache.cloudstack.api.command.QuotaConfigureEmailCmd;
|
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.QuotaValidateActivationRuleCmd;
|
||||||
|
import org.apache.cloudstack.discovery.ApiDiscoveryService;
|
||||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||||
|
import org.apache.cloudstack.jsinterpreter.JsInterpreterHelper;
|
||||||
import org.apache.cloudstack.quota.QuotaService;
|
import org.apache.cloudstack.quota.QuotaService;
|
||||||
import org.apache.cloudstack.quota.QuotaStatement;
|
import org.apache.cloudstack.quota.QuotaStatement;
|
||||||
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableDefinition;
|
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableDefinition;
|
||||||
@ -55,7 +61,7 @@ import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
|
|||||||
import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
|
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.discovery.ApiDiscoveryService;
|
import org.apache.cloudstack.utils.jsinterpreter.JsInterpreter;
|
||||||
|
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
|
|
||||||
@ -160,6 +166,12 @@ public class QuotaResponseBuilderImplTest extends TestCase {
|
|||||||
return new Calendar[] {calendar, calendar};
|
return new Calendar[] {calendar, calendar};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
QuotaValidateActivationRuleCmd quotaValidateActivationRuleCmdMock = Mockito.mock(QuotaValidateActivationRuleCmd.class);
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
JsInterpreterHelper jsInterpreterHelperMock = Mockito.mock(JsInterpreterHelper.class);
|
||||||
|
|
||||||
private QuotaTariffVO makeTariffTestData() {
|
private QuotaTariffVO makeTariffTestData() {
|
||||||
QuotaTariffVO tariffVO = new QuotaTariffVO();
|
QuotaTariffVO tariffVO = new QuotaTariffVO();
|
||||||
tariffVO.setUsageType(QuotaTypes.IP_ADDRESS);
|
tariffVO.setUsageType(QuotaTypes.IP_ADDRESS);
|
||||||
@ -645,4 +657,78 @@ public class QuotaResponseBuilderImplTest extends TestCase {
|
|||||||
|
|
||||||
assertFalse(quotaResponseBuilderSpy.isUserAllowedToSeeActivationRules(userMock));
|
assertFalse(quotaResponseBuilderSpy.isUserAllowedToSeeActivationRules(userMock));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void validateActivationRuleTestValidateActivationRuleReturnValidScriptResponse() {
|
||||||
|
Mockito.doReturn("if (account.name == 'test') { true } else { false }").when(quotaValidateActivationRuleCmdMock).getActivationRule();
|
||||||
|
Mockito.doReturn(QuotaTypes.getQuotaType(30)).when(quotaValidateActivationRuleCmdMock).getQuotaType();
|
||||||
|
Mockito.doReturn(quotaValidateActivationRuleCmdMock.getActivationRule()).when(jsInterpreterHelperMock).replaceScriptVariables(Mockito.anyString(), Mockito.any());
|
||||||
|
|
||||||
|
QuotaValidateActivationRuleResponse response = quotaResponseBuilderSpy.validateActivationRule(quotaValidateActivationRuleCmdMock);
|
||||||
|
|
||||||
|
Assert.assertTrue(response.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void validateActivationRuleTestUsageTypeIncompatibleVariableReturnInvalidScriptResponse() {
|
||||||
|
Mockito.doReturn("if (value.osName == 'test') { true } else { false }").when(quotaValidateActivationRuleCmdMock).getActivationRule();
|
||||||
|
Mockito.doReturn(QuotaTypes.getQuotaType(30)).when(quotaValidateActivationRuleCmdMock).getQuotaType();
|
||||||
|
Mockito.doReturn(quotaValidateActivationRuleCmdMock.getActivationRule()).when(jsInterpreterHelperMock).replaceScriptVariables(Mockito.anyString(), Mockito.any());
|
||||||
|
Mockito.when(jsInterpreterHelperMock.getScriptVariables(quotaValidateActivationRuleCmdMock.getActivationRule())).thenReturn(Set.of("value.osName"));
|
||||||
|
|
||||||
|
QuotaValidateActivationRuleResponse response = quotaResponseBuilderSpy.validateActivationRule(quotaValidateActivationRuleCmdMock);
|
||||||
|
|
||||||
|
Assert.assertFalse(response.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void validateActivationRuleTestActivationRuleWithSyntaxErrorsReturnInvalidScriptResponse() {
|
||||||
|
Mockito.doReturn("{ if (account.name == 'test') { true } else { false } }}").when(quotaValidateActivationRuleCmdMock).getActivationRule();
|
||||||
|
Mockito.doReturn(QuotaTypes.getQuotaType(1)).when(quotaValidateActivationRuleCmdMock).getQuotaType();
|
||||||
|
Mockito.doReturn(quotaValidateActivationRuleCmdMock.getActivationRule()).when(jsInterpreterHelperMock).replaceScriptVariables(Mockito.anyString(), Mockito.any());
|
||||||
|
|
||||||
|
QuotaValidateActivationRuleResponse response = quotaResponseBuilderSpy.validateActivationRule(quotaValidateActivationRuleCmdMock);
|
||||||
|
|
||||||
|
Assert.assertFalse(response.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isScriptVariablesValidTestUnsupportedUsageTypeVariablesReturnFalse() {
|
||||||
|
Set<String> scriptVariables = new HashSet<>(List.of("value.computingResources.cpuNumber", "account.name", "zone.id"));
|
||||||
|
List<String> usageTypeVariables = List.of("value.virtualSize", "account.name", "zone.id");
|
||||||
|
|
||||||
|
boolean isScriptVariablesValid = quotaResponseBuilderSpy.isScriptVariablesValid(scriptVariables, usageTypeVariables);
|
||||||
|
|
||||||
|
Assert.assertFalse(isScriptVariablesValid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isScriptVariablesValidTestSupportedUsageTypeVariablesReturnTrue() {
|
||||||
|
Set<String> scriptVariables = new HashSet<>(List.of("value.computingResources.cpuNumber", "account.name", "zone.id"));
|
||||||
|
List<String> usageTypeVariables = List.of("value.computingResources.cpuNumber", "account.name", "zone.id");
|
||||||
|
|
||||||
|
boolean isScriptVariablesValid = quotaResponseBuilderSpy.isScriptVariablesValid(scriptVariables, usageTypeVariables);
|
||||||
|
|
||||||
|
Assert.assertTrue(isScriptVariablesValid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isScriptVariablesValidTestVariablesUnrelatedToUsageTypeReturnTrue() {
|
||||||
|
Set<String> scriptVariables = new HashSet<>(List.of("variable1.valid", "variable2.valid.", "variable3.valid"));
|
||||||
|
List<String> usageTypeVariables = List.of("project.name", "account.id", "domain.path");
|
||||||
|
|
||||||
|
boolean isScriptVariablesValid = quotaResponseBuilderSpy.isScriptVariablesValid(scriptVariables, usageTypeVariables);
|
||||||
|
|
||||||
|
Assert.assertTrue(isScriptVariablesValid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void injectUsageTypeVariablesTestReturnInjectedVariables() {
|
||||||
|
JsInterpreter interpreter = Mockito.mock(JsInterpreter.class);
|
||||||
|
|
||||||
|
Map<String, String> formattedVariables = quotaResponseBuilderSpy.injectUsageTypeVariables(interpreter, List.of("account.name", "zone.name"));
|
||||||
|
|
||||||
|
Assert.assertTrue(formattedVariables.containsValue("accountname"));
|
||||||
|
Assert.assertTrue(formattedVariables.containsValue("zonename"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,240 @@
|
|||||||
|
// 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.jsinterpreter;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.openjdk.nashorn.api.scripting.ScriptUtils;
|
||||||
|
import org.openjdk.nashorn.internal.runtime.Context;
|
||||||
|
import org.openjdk.nashorn.internal.runtime.ErrorManager;
|
||||||
|
import org.openjdk.nashorn.internal.runtime.options.Options;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class JsInterpreterHelper {
|
||||||
|
private final Logger logger = LogManager.getLogger(getClass());
|
||||||
|
|
||||||
|
private static final String NAME = "name";
|
||||||
|
private static final String PROPERTY = "property";
|
||||||
|
private static final String TYPE = "type";
|
||||||
|
private static final String CALL_EXPRESSION = "CallExpression";
|
||||||
|
|
||||||
|
private int callExpressions;
|
||||||
|
|
||||||
|
private StringBuilder variable;
|
||||||
|
|
||||||
|
private Set<String> variables;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all variables from the given script.
|
||||||
|
*
|
||||||
|
* @param script the script to extract the variables.
|
||||||
|
* @return A {@link Set<String>} containing all variables in the script.
|
||||||
|
*/
|
||||||
|
public Set<String> getScriptVariables(String script) {
|
||||||
|
String parseTree = getScriptAsJsonTree(script);
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
JsonNode jsonNode = null;
|
||||||
|
variables = new HashSet<>();
|
||||||
|
variable = new StringBuilder();
|
||||||
|
|
||||||
|
try {
|
||||||
|
jsonNode = mapper.readTree(parseTree);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
logger.error("Unable to create the script JSON tree due to: [{}].", e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.trace("Searching script variables from [{}].", script);
|
||||||
|
iterateOverJsonTree(jsonNode.fields());
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(variable.toString())) {
|
||||||
|
logger.trace("Adding variable [{}] into the variables set.", variable);
|
||||||
|
removeCallFunctionsFromVariable();
|
||||||
|
variables.add(variable.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.trace("Found the following variables from the given script: [{}]", variables);
|
||||||
|
return variables;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getScriptAsJsonTree(String script) {
|
||||||
|
logger.trace("Creating JSON Tree for script [{}].", script);
|
||||||
|
Options options = new Options("nashorn");
|
||||||
|
options.set("anon.functions", true);
|
||||||
|
options.set("parse.only", true);
|
||||||
|
options.set("scripting", true);
|
||||||
|
|
||||||
|
ErrorManager errors = new ErrorManager();
|
||||||
|
Context context = new Context(options, errors, Thread.currentThread().getContextClassLoader());
|
||||||
|
Context.setGlobal(context.createGlobal());
|
||||||
|
|
||||||
|
return ScriptUtils.parse(script, "nashorn", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void iterateOverJsonTree(Iterator<Map.Entry<String, JsonNode>> iterator) {
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
iterateOverJsonTree(iterator.next());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void iterateOverJsonTree(Map.Entry<String, JsonNode> fields) {
|
||||||
|
JsonNode node = null;
|
||||||
|
|
||||||
|
if (fields.getValue().isArray()) {
|
||||||
|
iterateOverArrayNodes(fields);
|
||||||
|
} else {
|
||||||
|
node = fields.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
String fieldName = searchIntoObjectNodes(node);
|
||||||
|
|
||||||
|
if (fieldName == null) {
|
||||||
|
String key = fields.getKey();
|
||||||
|
if (TYPE.equals(key) && CALL_EXPRESSION.equals(node.textValue())) {
|
||||||
|
callExpressions++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NAME.equals(key) || PROPERTY.equals(key)) {
|
||||||
|
appendFieldValueToVariable(key, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void iterateOverArrayNodes(Map.Entry<String, JsonNode> fields) {
|
||||||
|
for (int count = 0; fields.getValue().get(count) != null; count++) {
|
||||||
|
iterateOverJsonTree(fields.getValue().get(count).fields());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String searchIntoObjectNodes(JsonNode node) {
|
||||||
|
if (node == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String fieldName = null;
|
||||||
|
Iterator<String> iterator = node.fieldNames();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
fieldName = iterator.next();
|
||||||
|
if (TYPE.equals(fieldName) && CALL_EXPRESSION.equals(node.get(fieldName).textValue())) {
|
||||||
|
callExpressions++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NAME.equals(fieldName) || PROPERTY.equals(fieldName)) {
|
||||||
|
appendFieldValueToVariable(fieldName, node.get(fieldName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.get(fieldName).isArray()) {
|
||||||
|
JsonNode blockStatementContent = node.get(fieldName).get(0);
|
||||||
|
if (blockStatementContent != null) {
|
||||||
|
iterateOverJsonTree(blockStatementContent.fields());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
iterateOverJsonTree(node.get(fieldName).fields());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void appendFieldValueToVariable(String key, JsonNode node) {
|
||||||
|
String nodeTextValue = node.textValue();
|
||||||
|
if (nodeTextValue == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PROPERTY.equals(key)) {
|
||||||
|
logger.trace("Appending field value [{}] to variable [{}] as the field name is \"property\".", nodeTextValue, variable);
|
||||||
|
variable.append(".").append(nodeTextValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.trace("Building new variable [{}] as the field name is \"name\"", nodeTextValue);
|
||||||
|
if (StringUtils.isNotBlank(variable.toString())) {
|
||||||
|
logger.trace("Adding variable [{}] into the variables set.", variable);
|
||||||
|
removeCallFunctionsFromVariable();
|
||||||
|
variables.add(variable.toString());
|
||||||
|
variable.setLength(0);
|
||||||
|
}
|
||||||
|
variable.append(nodeTextValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void removeCallFunctionsFromVariable() {
|
||||||
|
String[] disassembledVariable = variable.toString().split("\\.");
|
||||||
|
variable.setLength(0);
|
||||||
|
|
||||||
|
int newVariableSize = disassembledVariable.length - callExpressions;
|
||||||
|
String[] newVariable = Arrays.copyOfRange(disassembledVariable, 0, newVariableSize);
|
||||||
|
|
||||||
|
variable.append(String.join(".", newVariable));
|
||||||
|
callExpressions = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces all variables in script that matches the key in {@link Map} for their respective values.
|
||||||
|
*
|
||||||
|
* @param script the script which the variables will be replaced.
|
||||||
|
* @param variablesToReplace a {@link Map} which has the key as the variable to be replaced and the value as the variable to replace.
|
||||||
|
* @return A new script with the variables replaced.
|
||||||
|
*/
|
||||||
|
public String replaceScriptVariables(String script, Map<String, String> variablesToReplace) {
|
||||||
|
String regex = String.format("\\b(%s)\\b", String.join("|", variablesToReplace.keySet()));
|
||||||
|
Matcher matcher = Pattern.compile(regex).matcher(script);
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
while (matcher.find()) {
|
||||||
|
matcher.appendReplacement(sb, variablesToReplace.get(matcher.group()));
|
||||||
|
}
|
||||||
|
matcher.appendTail(sb);
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCallExpressions() {
|
||||||
|
return callExpressions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCallExpressions(int callExpressions) {
|
||||||
|
this.callExpressions = callExpressions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringBuilder getVariable() {
|
||||||
|
return variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVariable(StringBuilder variable) {
|
||||||
|
this.variable = variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getVariables() {
|
||||||
|
return variables;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVariables(Set<String> variables) {
|
||||||
|
this.variables = variables;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -218,6 +218,8 @@
|
|||||||
|
|
||||||
<bean id="snapshotHelper" class="org.apache.cloudstack.snapshot.SnapshotHelper" />
|
<bean id="snapshotHelper" class="org.apache.cloudstack.snapshot.SnapshotHelper" />
|
||||||
|
|
||||||
|
<bean id="jsInterpreterHelper" class="org.apache.cloudstack.jsinterpreter.JsInterpreterHelper" />
|
||||||
|
|
||||||
<bean id="uploadMonitorImpl" class="com.cloud.storage.upload.UploadMonitorImpl" />
|
<bean id="uploadMonitorImpl" class="com.cloud.storage.upload.UploadMonitorImpl" />
|
||||||
<bean id="usageServiceImpl" class="com.cloud.usage.UsageServiceImpl" />
|
<bean id="usageServiceImpl" class="com.cloud.usage.UsageServiceImpl" />
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,228 @@
|
|||||||
|
// 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.jsinterpreter;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.Spy;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class JsInterpreterHelperTest {
|
||||||
|
|
||||||
|
@Spy
|
||||||
|
private JsInterpreterHelper jsInterpreterHelperSpy;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private JsonNode jsonNodeMock;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private Iterator<String> fieldNamesMock;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private Map.Entry<String, JsonNode> fields;
|
||||||
|
|
||||||
|
public void setupIterateOverJsonTreeTests() {
|
||||||
|
JsonNode node = Map.entry("array", jsonNodeMock).getValue();
|
||||||
|
Mockito.doReturn(true).when(node).isArray();
|
||||||
|
Mockito.doReturn(node).when(fields).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getScriptVariablesTestReturnVariables() {
|
||||||
|
String script = "if (account.name == 'test') { domain.id } else { zone.id }";
|
||||||
|
|
||||||
|
Set<String> variables = jsInterpreterHelperSpy.getScriptVariables(script);
|
||||||
|
|
||||||
|
Assert.assertEquals(variables.size(), 3);
|
||||||
|
Assert.assertTrue(variables.containsAll(List.of("account.name", "domain.id", "zone.id")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getScriptVariablesTestScriptWithoutVariablesReturnEmptyList() {
|
||||||
|
String script = "if (4 < 2) { 3 } else if (3 != 3) { 3 } else { 3 > 3 } while (false) { 3 }";
|
||||||
|
|
||||||
|
Set<String> variables = jsInterpreterHelperSpy.getScriptVariables(script);
|
||||||
|
|
||||||
|
Assert.assertTrue(variables.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void replaceScriptVariablesTestReturnScriptWithVariablesReplaced() {
|
||||||
|
String script = "if (account.name == 'test') { domain.id } else { zone.id }";
|
||||||
|
Map<String, String> newVariables = new HashMap<>();
|
||||||
|
newVariables.put("account.name", "accountname");
|
||||||
|
newVariables.put("domain.id", "domainid");
|
||||||
|
newVariables.put("zone.id", "zoneid");
|
||||||
|
|
||||||
|
String newScript = jsInterpreterHelperSpy.replaceScriptVariables(script, newVariables);
|
||||||
|
|
||||||
|
Assert.assertEquals("if (accountname == 'test') { domainid } else { zoneid }", newScript);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void searchIntoObjectNodesTestNullNodeReturnNull() {
|
||||||
|
String fieldName = jsInterpreterHelperSpy.searchIntoObjectNodes(null);
|
||||||
|
|
||||||
|
Assert.assertEquals(null, fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void searchIntoObjectNodesTestNonEmptyFieldNamesReturnFieldName() {
|
||||||
|
Mockito.doReturn(true, false).when(fieldNamesMock).hasNext();
|
||||||
|
Mockito.doReturn("fieldName").when(fieldNamesMock).next();
|
||||||
|
Mockito.doReturn(jsonNodeMock).when(jsonNodeMock).get("fieldName");
|
||||||
|
Mockito.doNothing().when(jsInterpreterHelperSpy).iterateOverJsonTree((Iterator<Map.Entry<String, JsonNode>>) Mockito.any());
|
||||||
|
Mockito.doReturn(fieldNamesMock).when(jsonNodeMock).fieldNames();
|
||||||
|
|
||||||
|
String fieldName = jsInterpreterHelperSpy.searchIntoObjectNodes(jsonNodeMock);
|
||||||
|
|
||||||
|
Assert.assertEquals("fieldName", fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void searchIntoObjectNodesTestNameFieldAppendFieldValueToVariable() {
|
||||||
|
Mockito.doReturn(true, false).when(fieldNamesMock).hasNext();
|
||||||
|
Mockito.doReturn("name").when(fieldNamesMock).next();
|
||||||
|
Mockito.doReturn(jsonNodeMock).when(jsonNodeMock).get("name");
|
||||||
|
Mockito.doNothing().when(jsInterpreterHelperSpy).iterateOverJsonTree((Iterator<Map.Entry<String, JsonNode>>) Mockito.any());
|
||||||
|
Mockito.doReturn(fieldNamesMock).when(jsonNodeMock).fieldNames();
|
||||||
|
|
||||||
|
jsInterpreterHelperSpy.searchIntoObjectNodes(jsonNodeMock);
|
||||||
|
|
||||||
|
Mockito.verify(jsInterpreterHelperSpy, Mockito.times(1)).appendFieldValueToVariable(Mockito.any(), Mockito.any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void searchIntoObjectNodesTestPropertyFieldAppendFieldValueToVariable() {
|
||||||
|
Mockito.doReturn(true, false).when(fieldNamesMock).hasNext();
|
||||||
|
Mockito.doReturn("property").when(fieldNamesMock).next();
|
||||||
|
Mockito.doReturn(jsonNodeMock).when(jsonNodeMock).get("property");
|
||||||
|
Mockito.doNothing().when(jsInterpreterHelperSpy).iterateOverJsonTree((Iterator<Map.Entry<String, JsonNode>>) Mockito.any());
|
||||||
|
Mockito.doReturn(fieldNamesMock).when(jsonNodeMock).fieldNames();
|
||||||
|
|
||||||
|
jsInterpreterHelperSpy.searchIntoObjectNodes(jsonNodeMock);
|
||||||
|
|
||||||
|
Mockito.verify(jsInterpreterHelperSpy, Mockito.times(1)).appendFieldValueToVariable(Mockito.any(), Mockito.any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void appendFieldValueToVariableTestPropertyKeyAppendFieldValueAsAttribute() {
|
||||||
|
jsInterpreterHelperSpy.setVariable(new StringBuilder("account"));
|
||||||
|
jsInterpreterHelperSpy.setVariables(new HashSet<>());
|
||||||
|
Mockito.doReturn("name").when(jsonNodeMock).textValue();
|
||||||
|
|
||||||
|
jsInterpreterHelperSpy.appendFieldValueToVariable("property", jsonNodeMock);
|
||||||
|
|
||||||
|
Assert.assertEquals("account.name", jsInterpreterHelperSpy.getVariable().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void appendFieldValueToVariableTestNameKeyAppendFieldValueAsNewVariable() {
|
||||||
|
jsInterpreterHelperSpy.setVariable(new StringBuilder("account"));
|
||||||
|
jsInterpreterHelperSpy.setVariables(new HashSet<>());
|
||||||
|
Mockito.doReturn("zone").when(jsonNodeMock).textValue();
|
||||||
|
|
||||||
|
jsInterpreterHelperSpy.appendFieldValueToVariable("name", jsonNodeMock);
|
||||||
|
|
||||||
|
Assert.assertEquals("zone", jsInterpreterHelperSpy.getVariable().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void iterateOverJsonTreeTestMethodsCall() {
|
||||||
|
setupIterateOverJsonTreeTests();
|
||||||
|
|
||||||
|
jsInterpreterHelperSpy.iterateOverJsonTree(fields);
|
||||||
|
|
||||||
|
Mockito.verify(jsInterpreterHelperSpy, Mockito.times(1)).iterateOverArrayNodes(Mockito.any());
|
||||||
|
Mockito.verify(jsInterpreterHelperSpy, Mockito.times(1)).searchIntoObjectNodes(Mockito.any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void iterateOverJsonTreeTestFieldNameNullAndNameKeyAppendFieldValueToVariable() {
|
||||||
|
setupIterateOverJsonTreeTests();
|
||||||
|
Mockito.doReturn("name").when(fields).getKey();
|
||||||
|
Mockito.doNothing().when(jsInterpreterHelperSpy).appendFieldValueToVariable(Mockito.any(), Mockito.any());
|
||||||
|
|
||||||
|
jsInterpreterHelperSpy.iterateOverJsonTree(fields);
|
||||||
|
|
||||||
|
Mockito.verify(jsInterpreterHelperSpy, Mockito.times(1)).appendFieldValueToVariable(Mockito.any(), Mockito.any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void iterateOverJsonTreeTestFieldNameNullAndNamePropertyAppendFieldValueToVariable() {
|
||||||
|
setupIterateOverJsonTreeTests();
|
||||||
|
Mockito.doReturn("property").when(fields).getKey();
|
||||||
|
Mockito.doNothing().when(jsInterpreterHelperSpy).appendFieldValueToVariable(Mockito.any(), Mockito.any());
|
||||||
|
|
||||||
|
jsInterpreterHelperSpy.iterateOverJsonTree(fields);
|
||||||
|
|
||||||
|
Mockito.verify(jsInterpreterHelperSpy, Mockito.times(1)).appendFieldValueToVariable(Mockito.any(), Mockito.any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void iterateOverArrayNodesTestThreeSizeArrayCallIterateOverJsonTreeThreeTimes() {
|
||||||
|
Map<String, JsonNode> fields = new HashMap<>();
|
||||||
|
fields.put("field", jsonNodeMock);
|
||||||
|
|
||||||
|
JsonNode root = Mockito.mock(JsonNode.class);
|
||||||
|
JsonNode node1 = Mockito.mock(JsonNode.class);
|
||||||
|
JsonNode node2 = Mockito.mock(JsonNode.class);
|
||||||
|
JsonNode node3 = Mockito.mock(JsonNode.class);
|
||||||
|
Mockito.doReturn(fields.entrySet().iterator()).when(node1).fields();
|
||||||
|
Mockito.doReturn(fields.entrySet().iterator()).when(node2).fields();
|
||||||
|
Mockito.doReturn(fields.entrySet().iterator()).when(node3).fields();
|
||||||
|
|
||||||
|
Map<String, JsonNode> childrenMap = new HashMap<>();
|
||||||
|
childrenMap.put("node1", node1);
|
||||||
|
childrenMap.put("node2", node2);
|
||||||
|
childrenMap.put("node3", node3);
|
||||||
|
|
||||||
|
Map.Entry<String, JsonNode> rootEntry = Map.entry("rootNode", root);
|
||||||
|
|
||||||
|
Mockito.doReturn(node1).when(rootEntry.getValue()).get(0);
|
||||||
|
Mockito.doReturn(node2).when(rootEntry.getValue()).get(1);
|
||||||
|
Mockito.doReturn(node3).when(rootEntry.getValue()).get(2);
|
||||||
|
Mockito.doNothing().when(jsInterpreterHelperSpy).iterateOverJsonTree((Iterator<Map.Entry<String, JsonNode>>) Mockito.any());
|
||||||
|
|
||||||
|
jsInterpreterHelperSpy.iterateOverArrayNodes(rootEntry);
|
||||||
|
|
||||||
|
Mockito.verify(jsInterpreterHelperSpy, Mockito.times(3)).iterateOverJsonTree((Iterator<Map.Entry<String, JsonNode>>) Mockito.any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void removeCallFunctionsFromVariableTestTwoCallExpressionsRemoveTwoLastProperties() {
|
||||||
|
jsInterpreterHelperSpy.setCallExpressions(2);
|
||||||
|
jsInterpreterHelperSpy.setVariable(new StringBuilder("value.osName.toLowerCase().indexOf('windows')"));
|
||||||
|
|
||||||
|
jsInterpreterHelperSpy.removeCallFunctionsFromVariable();
|
||||||
|
|
||||||
|
Assert.assertEquals("value.osName", jsInterpreterHelperSpy.getVariable().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1803,6 +1803,7 @@
|
|||||||
"label.quota.statement.tariff": "Quota tariff",
|
"label.quota.statement.tariff": "Quota tariff",
|
||||||
"label.quota.summary": "Summary",
|
"label.quota.summary": "Summary",
|
||||||
"label.quota.tariff": "Tariff",
|
"label.quota.tariff": "Tariff",
|
||||||
|
"label.quota.tariff.activationrule": "Activation rule",
|
||||||
"label.quota.tariff.effectivedate": "Effective date",
|
"label.quota.tariff.effectivedate": "Effective date",
|
||||||
"label.quota.tariff.position": "Position",
|
"label.quota.tariff.position": "Position",
|
||||||
"label.quota.tariff.value": "Tariff value",
|
"label.quota.tariff.value": "Tariff value",
|
||||||
@ -1810,6 +1811,7 @@
|
|||||||
"label.quota.type.name": "Usage Type",
|
"label.quota.type.name": "Usage Type",
|
||||||
"label.quota.type.unit": "Usage unit",
|
"label.quota.type.unit": "Usage unit",
|
||||||
"label.quota.usage": "Quota consumption",
|
"label.quota.usage": "Quota consumption",
|
||||||
|
"label.quota.validate.activation.rule": "Validate activation rule",
|
||||||
"label.quota.value": "Quota value",
|
"label.quota.value": "Quota value",
|
||||||
"label.quotastate": "Quota state",
|
"label.quotastate": "Quota state",
|
||||||
"label.quota_enforce": "Enforce Quota",
|
"label.quota_enforce": "Enforce Quota",
|
||||||
@ -3695,6 +3697,7 @@
|
|||||||
"migrate.from": "Migrate from",
|
"migrate.from": "Migrate from",
|
||||||
"migrate.to": "Migrate to",
|
"migrate.to": "Migrate to",
|
||||||
"migrationPolicy": "Migration policy",
|
"migrationPolicy": "Migration policy",
|
||||||
|
"placeholder.quota.tariff.activationrule": "Quota tariff's activation rule",
|
||||||
"placeholder.quota.tariff.description": "Quota tariff's description",
|
"placeholder.quota.tariff.description": "Quota tariff's description",
|
||||||
"placeholder.quota.tariff.enddate": "Quota tariff's end date",
|
"placeholder.quota.tariff.enddate": "Quota tariff's end date",
|
||||||
"placeholder.quota.tariff.name": "Quota tariff's name",
|
"placeholder.quota.tariff.name": "Quota tariff's name",
|
||||||
|
|||||||
@ -128,6 +128,9 @@
|
|||||||
"label.action.migrate.systemvm.to.ps": "Migrar VM de sistema para outro armazenamento prim\u00e1rio",
|
"label.action.migrate.systemvm.to.ps": "Migrar VM de sistema para outro armazenamento prim\u00e1rio",
|
||||||
"label.action.project.add.account": "Adicionar conta ao projeto",
|
"label.action.project.add.account": "Adicionar conta ao projeto",
|
||||||
"label.action.project.add.user": "Adicionar usu\u00e1rio a um projeto",
|
"label.action.project.add.user": "Adicionar usu\u00e1rio a um projeto",
|
||||||
|
"label.action.quota.tariff.create": "Criar tarifa",
|
||||||
|
"label.action.quota.tariff.edit": "Editar tarifa",
|
||||||
|
"label.action.quota.tariff.remove": "Remover tarifa",
|
||||||
"label.action.reboot.instance": "Reiniciar inst\u00e2ncia",
|
"label.action.reboot.instance": "Reiniciar inst\u00e2ncia",
|
||||||
"label.action.reboot.router": "Reiniciar roteador",
|
"label.action.reboot.router": "Reiniciar roteador",
|
||||||
"label.action.reboot.systemvm": "Reiniciar VM de sistema",
|
"label.action.reboot.systemvm": "Reiniciar VM de sistema",
|
||||||
@ -1283,9 +1286,7 @@
|
|||||||
"label.quotastate": "Estado da cota",
|
"label.quotastate": "Estado da cota",
|
||||||
"label.summary": "Sum\u00e1rio",
|
"label.summary": "Sum\u00e1rio",
|
||||||
"label.quota.tariff": "Tarifa",
|
"label.quota.tariff": "Tarifa",
|
||||||
"label.action.quota.tariff.create": "Criar tarifa",
|
"label.quota.tariff.activationrule": "Regra de ativa\u00e7\u00e3o",
|
||||||
"label.action.quota.tariff.edit": "Editar tarifa",
|
|
||||||
"label.action.quota.tariff.remove": "Remover tarifa",
|
|
||||||
"label.quota.tariff.effectivedate": "Data efetiva",
|
"label.quota.tariff.effectivedate": "Data efetiva",
|
||||||
"label.quota.tariff.position": "Posi\u00e7\u00e3o",
|
"label.quota.tariff.position": "Posi\u00e7\u00e3o",
|
||||||
"label.quota.tariff.value": "Valor",
|
"label.quota.tariff.value": "Valor",
|
||||||
@ -1293,6 +1294,7 @@
|
|||||||
"label.quota.type.name": "Tipo de uso",
|
"label.quota.type.name": "Tipo de uso",
|
||||||
"label.quota.type.unit": "Unidade do uso",
|
"label.quota.type.unit": "Unidade do uso",
|
||||||
"label.quota.usage": "Consumo da cota",
|
"label.quota.usage": "Consumo da cota",
|
||||||
|
"label.quota.validate.activation.rule": "Validar regra de ativa\u00e7\u00e3o",
|
||||||
"label.quota.value": "Valor",
|
"label.quota.value": "Valor",
|
||||||
"label.rados.monitor": "Monitor RADOS",
|
"label.rados.monitor": "Monitor RADOS",
|
||||||
"label.rados.pool": "Pool do RADOS",
|
"label.rados.pool": "Pool do RADOS",
|
||||||
@ -2514,6 +2516,7 @@
|
|||||||
"migrate.from": "Migrar de",
|
"migrate.from": "Migrar de",
|
||||||
"migrate.to": "Migrar para",
|
"migrate.to": "Migrar para",
|
||||||
"migrationPolicy": "Pol\u00edtica de migra\u00e7\u00e3o",
|
"migrationPolicy": "Pol\u00edtica de migra\u00e7\u00e3o",
|
||||||
|
"placeholder.quota.tariff.activationrule": "Regra de ativa\u00e7\u00e3o",
|
||||||
"placeholder.quota.tariff.description": "Descri\u00e7\u00e3o",
|
"placeholder.quota.tariff.description": "Descri\u00e7\u00e3o",
|
||||||
"placeholder.quota.tariff.enddate": "Data de t\u00e9rmino",
|
"placeholder.quota.tariff.enddate": "Data de t\u00e9rmino",
|
||||||
"placeholder.quota.tariff.name": "Nome",
|
"placeholder.quota.tariff.name": "Nome",
|
||||||
|
|||||||
@ -24,6 +24,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.border-success {
|
||||||
|
border-color: #349469;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-fail {
|
||||||
|
border-color: #dc3545;
|
||||||
|
}
|
||||||
|
|
||||||
.form textarea {
|
.form textarea {
|
||||||
resize: both;
|
resize: both;
|
||||||
min-width: 20vw;
|
min-width: 20vw;
|
||||||
|
|||||||
@ -66,6 +66,20 @@
|
|||||||
v-model:value="form.value"
|
v-model:value="form.value"
|
||||||
:placeholder="$t('placeholder.quota.tariff.value')" />
|
:placeholder="$t('placeholder.quota.tariff.value')" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item ref="activationRule" name="activationRule">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.quota.tariff.activationrule')" :tooltip="apiParams.activationrule.description"/>
|
||||||
|
</template>
|
||||||
|
<a-textarea
|
||||||
|
v-model:value="form.activationRule"
|
||||||
|
:placeholder="$t('placeholder.quota.tariff.activationrule')"
|
||||||
|
:class="stateBorder"
|
||||||
|
:max-length="65535"
|
||||||
|
@keydown="isActivationRuleValid = undefined" />
|
||||||
|
</a-form-item>
|
||||||
|
<div class="action-button">
|
||||||
|
<a-button type="primary" @click="handleValidateActivationRule">{{ $t('label.quota.validate.activation.rule') }}</a-button>
|
||||||
|
</div>
|
||||||
<a-form-item ref="position" name="position">
|
<a-form-item ref="position" name="position">
|
||||||
<template #label>
|
<template #label>
|
||||||
<tooltip-label :title="$t('label.quota.tariff.position')" :tooltip="apiParams.position.description" />
|
<tooltip-label :title="$t('label.quota.tariff.position')" :tooltip="apiParams.position.description" />
|
||||||
@ -124,7 +138,16 @@ export default {
|
|||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
dayjs
|
dayjs,
|
||||||
|
isActivationRuleValid: undefined
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
stateBorder () {
|
||||||
|
return {
|
||||||
|
'border-success': this.isActivationRuleValid,
|
||||||
|
'border-fail': this.isActivationRuleValid === false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeCreate () {
|
beforeCreate () {
|
||||||
@ -180,6 +203,33 @@ export default {
|
|||||||
this.formRef.value.scrollToField(error.errorFields[0].name)
|
this.formRef.value.scrollToField(error.errorFields[0].name)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
handleValidateActivationRule (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
if (this.loading) return
|
||||||
|
|
||||||
|
const formRaw = toRaw(this.form)
|
||||||
|
const values = this.handleRemoveFields(formRaw)
|
||||||
|
|
||||||
|
this.loading = true
|
||||||
|
api('quotaValidateActivationRule', {}, 'POST', {
|
||||||
|
activationRule: values.activationRule || ' ',
|
||||||
|
usageType: values?.usageType?.split('-')[0]
|
||||||
|
}).then(response => {
|
||||||
|
const shortResponse = response.quotavalidateactivationruleresponse.validactivationrule
|
||||||
|
|
||||||
|
if (shortResponse.isvalid) {
|
||||||
|
this.$message.success(shortResponse.message)
|
||||||
|
} else {
|
||||||
|
this.$message.error(shortResponse.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isActivationRuleValid = shortResponse.isvalid
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
closeModal () {
|
closeModal () {
|
||||||
this.$emit('close-action')
|
this.$emit('close-action')
|
||||||
},
|
},
|
||||||
|
|||||||
@ -42,6 +42,20 @@
|
|||||||
v-model:value="form.value"
|
v-model:value="form.value"
|
||||||
:placeholder="$t('placeholder.quota.tariff.value')" />
|
:placeholder="$t('placeholder.quota.tariff.value')" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item ref="activationRule" name="activationRule">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.quota.tariff.activationrule')" :tooltip="apiParams.activationrule.description"/>
|
||||||
|
</template>
|
||||||
|
<a-textarea
|
||||||
|
v-model:value="form.activationRule"
|
||||||
|
:placeholder="$t('placeholder.quota.tariff.activationrule')"
|
||||||
|
:class="stateBorder"
|
||||||
|
:max-length="65535"
|
||||||
|
@keydown="isActivationRuleValid = undefined" />
|
||||||
|
</a-form-item>
|
||||||
|
<div class="action-button">
|
||||||
|
<a-button type="primary" @click="handleValidateActivationRule">{{ $t('label.quota.validate.activation.rule') }}</a-button>
|
||||||
|
</div>
|
||||||
<a-form-item ref="position" name="position">
|
<a-form-item ref="position" name="position">
|
||||||
<template #label>
|
<template #label>
|
||||||
<tooltip-label :title="$t('label.quota.tariff.position')" :tooltip="apiParams.position.description"/>
|
<tooltip-label :title="$t('label.quota.tariff.position')" :tooltip="apiParams.position.description"/>
|
||||||
@ -93,8 +107,17 @@ export default {
|
|||||||
},
|
},
|
||||||
data: () => ({
|
data: () => ({
|
||||||
loading: false,
|
loading: false,
|
||||||
dayjs
|
dayjs,
|
||||||
|
isActivationRuleValid: undefined
|
||||||
}),
|
}),
|
||||||
|
computed: {
|
||||||
|
stateBorder () {
|
||||||
|
return {
|
||||||
|
'border-success': this.isActivationRuleValid,
|
||||||
|
'border-fail': this.isActivationRuleValid === false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
inject: ['parentFetchData'],
|
inject: ['parentFetchData'],
|
||||||
beforeCreate () {
|
beforeCreate () {
|
||||||
this.apiParams = this.$getApiParams('quotaTariffUpdate')
|
this.apiParams = this.$getApiParams('quotaTariffUpdate')
|
||||||
@ -109,7 +132,8 @@ export default {
|
|||||||
description: this.resource.description,
|
description: this.resource.description,
|
||||||
value: this.resource.tariffValue,
|
value: this.resource.tariffValue,
|
||||||
position: this.resource.position,
|
position: this.resource.position,
|
||||||
endDate: parseDateToDatePicker(this.resource.endDate)
|
endDate: parseDateToDatePicker(this.resource.endDate),
|
||||||
|
activationRule: this.resource.activationRule
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
closeModal () {
|
closeModal () {
|
||||||
@ -143,6 +167,10 @@ export default {
|
|||||||
params.enddate = parseDayJsObject({ value: values.endDate })
|
params.enddate = parseDayJsObject({ value: values.endDate })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (values.activationRule && this.resource.activationRule !== values.activationRule) {
|
||||||
|
params.activationRule = values.activationRule
|
||||||
|
}
|
||||||
|
|
||||||
if (Object.keys(params).length === 1) {
|
if (Object.keys(params).length === 1) {
|
||||||
this.closeModal()
|
this.closeModal()
|
||||||
return
|
return
|
||||||
@ -178,6 +206,33 @@ export default {
|
|||||||
return current < startOfToday || current < lowerEndDateLimit.startOf('day')
|
return current < startOfToday || current < lowerEndDateLimit.startOf('day')
|
||||||
}
|
}
|
||||||
return current < startOfToday || current < lowerEndDateLimit.utc(false).startOf('day')
|
return current < startOfToday || current < lowerEndDateLimit.utc(false).startOf('day')
|
||||||
|
},
|
||||||
|
handleValidateActivationRule (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
if (this.loading) return
|
||||||
|
|
||||||
|
const formRaw = toRaw(this.form)
|
||||||
|
const values = this.handleRemoveFields(formRaw)
|
||||||
|
|
||||||
|
this.loading = true
|
||||||
|
api('quotaValidateActivationRule', {}, 'POST', {
|
||||||
|
activationRule: values.activationRule || ' ',
|
||||||
|
usageType: this.resource.usageType
|
||||||
|
}).then(response => {
|
||||||
|
const shortResponse = response.quotavalidateactivationruleresponse.validactivationrule
|
||||||
|
|
||||||
|
if (shortResponse.isvalid) {
|
||||||
|
this.$message.success(shortResponse.message)
|
||||||
|
} else {
|
||||||
|
this.$message.error(shortResponse.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isActivationRuleValid = shortResponse.isvalid
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user