refactor(测试跟踪): 优化功能用例导入校验
--story=1008224 --user=陈建星 用例导出/导入支持自定义字段 https://www.tapd.cn/55049933/s/1225482
This commit is contained in:
parent
f31e835893
commit
4eb0348e6c
|
@ -1,13 +1,20 @@
|
|||
package io.metersphere.commons.constants;
|
||||
|
||||
public enum CustomFieldType {
|
||||
INPUT("input"),TEXTAREA("textarea"),
|
||||
SELECT("select"),MULTIPLE_SELECT("multipleSelect"),
|
||||
RADIO("radio"),CHECKBOX("checkbox"),
|
||||
MEMBER("member"), MULTIPLE_MEMBER("multipleMember"),
|
||||
DATE("date"),DATETIME("datetime"),
|
||||
INT("int"),FLOAT("float"),
|
||||
MULTIPLE_INPUT("multipleInput"),RICH_TEXT("richText");
|
||||
INPUT("input"),
|
||||
TEXTAREA("textarea"),
|
||||
SELECT("select"),
|
||||
MULTIPLE_SELECT("multipleSelect"),
|
||||
RADIO("radio"),
|
||||
CHECKBOX("checkbox"),
|
||||
MEMBER("member"),
|
||||
MULTIPLE_MEMBER("multipleMember"),
|
||||
DATE("date"),
|
||||
DATETIME("datetime"),
|
||||
INT("int"),
|
||||
FLOAT("float"),
|
||||
MULTIPLE_INPUT("multipleInput"),
|
||||
RICH_TEXT("richText");
|
||||
|
||||
String value;
|
||||
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
package io.metersphere.commons.constants;
|
||||
|
||||
public enum TestCaseStatus {
|
||||
performance,api,testcase,automation
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package io.metersphere.commons.constants;
|
||||
|
||||
public enum TestCaseTestStatus {
|
||||
performance, api, testcase, automation
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package io.metersphere.commons.exception;
|
||||
|
||||
public class CustomFieldValidateException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public CustomFieldValidateException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public static void throwException(String message) throws CustomFieldValidateException {
|
||||
throw new CustomFieldValidateException(message);
|
||||
}
|
||||
}
|
|
@ -6,6 +6,15 @@ import lombok.Setter;
|
|||
@Setter
|
||||
@Getter
|
||||
public class CustomFieldOption {
|
||||
private String text;
|
||||
private String value;
|
||||
private String text;
|
||||
private Boolean system = false;
|
||||
|
||||
public CustomFieldOption(String value, String text, Boolean system) {
|
||||
this.text = text;
|
||||
this.value = value;
|
||||
this.system = system;
|
||||
}
|
||||
|
||||
public CustomFieldOption() {}
|
||||
}
|
||||
|
|
|
@ -14,35 +14,41 @@ import java.util.function.Function;
|
|||
|
||||
public enum TestCaseImportFiled {
|
||||
|
||||
ID("ID", "ID", "ID", TestCaseExcelData::getCustomNum),
|
||||
NAME("用例名称", "用例名稱", "Name", TestCaseExcelData::getName),
|
||||
MODULE("所属模块", "所屬模塊", "Module", TestCaseExcelData::getNodePath),
|
||||
TAG("标签", "標簽", "Tag", TestCaseImportFiled::parseTags),
|
||||
PREREQUISITE("前置条件", "前置條件", "Prerequisite", TestCaseExcelData::getPrerequisite),
|
||||
REMARK("备注", "備註", "Remark", TestCaseExcelData::getRemark),
|
||||
STEP_DESC("步骤描述", "步驟描述", "Step description", TestCaseExcelData::getStepDesc),
|
||||
STEP_RESULT("预期结果", "預期結果", "Step result", TestCaseExcelData::getStepResult),
|
||||
STEP_MODEL("编辑模式", "編輯模式", "Edit Model", TestCaseExcelData::getStepModel),
|
||||
STATUS("用例状态", "用例狀態", "Case status", TestCaseExcelData::getStatus),
|
||||
MAINTAINER("责任人", "責任人", "Maintainer", TestCaseExcelData::getMaintainer),
|
||||
PRIORITY("用例等级", "用例等級", "Priority", TestCaseExcelData::getPriority);
|
||||
ID("id", "ID", "ID", "ID", TestCaseExcelData::getCustomNum),
|
||||
NAME("name", "用例名称", "用例名稱", "Name", TestCaseExcelData::getName),
|
||||
MODULE("module","所属模块", "所屬模塊", "Module", TestCaseExcelData::getNodePath),
|
||||
TAGS("tags","标签", "標簽", "Tag", TestCaseImportFiled::parseTags),
|
||||
PREREQUISITE("prerequisite","前置条件", "前置條件", "Prerequisite", TestCaseExcelData::getPrerequisite),
|
||||
REMARK("remark","备注", "備註", "Remark", TestCaseExcelData::getRemark),
|
||||
STEP_DESC("stepDesc","步骤描述", "步驟描述", "Step description", TestCaseExcelData::getStepDesc),
|
||||
STEP_RESULT("stepResult","预期结果", "預期結果", "Step result", TestCaseExcelData::getStepResult),
|
||||
STEP_MODEL("stepModel","编辑模式", "編輯模式", "Edit Model", TestCaseExcelData::getStepModel),
|
||||
STATUS("status","用例状态", "用例狀態", "Case status", TestCaseExcelData::getStatus),
|
||||
MAINTAINER("maintainer","责任人", "責任人", "Maintainer", TestCaseExcelData::getMaintainer),
|
||||
PRIORITY("priority","用例等级", "用例等級", "Priority", TestCaseExcelData::getPriority);
|
||||
|
||||
private Map<Locale, String> filedLangMap;
|
||||
private Function<TestCaseExcelData, String> parseFunc;
|
||||
private String value;
|
||||
|
||||
public Map<Locale, String> getFiledLangMap() {
|
||||
return this.filedLangMap;
|
||||
}
|
||||
|
||||
TestCaseImportFiled(String zn, String chineseTw, String us, Function<TestCaseExcelData, String> parseFunc) {
|
||||
TestCaseImportFiled(String value, String zn, String chineseTw, String us, Function<TestCaseExcelData, String> parseFunc) {
|
||||
this.filedLangMap = new HashMap<>() {{
|
||||
put(Locale.SIMPLIFIED_CHINESE, zn);
|
||||
put(Locale.TRADITIONAL_CHINESE, chineseTw);
|
||||
put(Locale.US, us);
|
||||
}};
|
||||
this.value = value;
|
||||
this.parseFunc = parseFunc;
|
||||
}
|
||||
|
||||
public Map<Locale, String> getFiledLangMap() {
|
||||
return this.filedLangMap;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String parseExcelDataValue(TestCaseExcelData excelData) {
|
||||
return parseFunc.apply(excelData);
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ public class TestCaseExcelData {
|
|||
@ExcelIgnore
|
||||
private String priority;
|
||||
@ExcelIgnore
|
||||
Map<String, String> customDatas = new LinkedHashMap<>();
|
||||
Map<String, String> customData = new LinkedHashMap<>();
|
||||
|
||||
@ExcelIgnore
|
||||
List<String> mergeStepDesc;
|
||||
|
|
|
@ -61,18 +61,6 @@ public class TestCaseExcelDataCn extends TestCaseExcelData {
|
|||
@Pattern(regexp = "(^TEXT$)|(^STEP$)|(.{0})", message = "{test_case_step_model_validate}")
|
||||
private String stepModel;
|
||||
|
||||
@ColumnWidth(50)
|
||||
@ExcelProperty("用例状态")
|
||||
private String status;
|
||||
|
||||
@ExcelProperty(value = "责任人(ID)")
|
||||
private String maintainer;
|
||||
|
||||
@NotBlank(message = "{cannot_be_null}")
|
||||
@ExcelProperty("用例等级")
|
||||
@Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "{test_case_priority_validate}")
|
||||
private String priority;
|
||||
|
||||
@Override
|
||||
public List<List<String>> getHead(boolean needNum, List<CustomFieldDao> customFields) {
|
||||
return super.getHead(needNum, customFields, Locale.SIMPLIFIED_CHINESE);
|
||||
|
|
|
@ -61,18 +61,6 @@ public class TestCaseExcelDataTw extends TestCaseExcelData {
|
|||
@Pattern(regexp = "(^TEXT$)|(^STEP$)|(.{0})", message = "{test_case_step_model_validate}")
|
||||
private String stepModel;
|
||||
|
||||
@ColumnWidth(50)
|
||||
@ExcelProperty("用例狀態")
|
||||
private String status;
|
||||
|
||||
@NotBlank(message = "{cannot_be_null}")
|
||||
@ExcelProperty("用例等級")
|
||||
@Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "{test_case_priority_validate}")
|
||||
private String priority;
|
||||
|
||||
@ExcelProperty("責任人(ID)")
|
||||
private String maintainer;
|
||||
|
||||
@Override
|
||||
public List<List<String>> getHead(boolean needNum, List<CustomFieldDao> customFields) {
|
||||
return super.getHead(needNum, customFields, Locale.TRADITIONAL_CHINESE);
|
||||
|
|
|
@ -62,17 +62,6 @@ public class TestCaseExcelDataUs extends TestCaseExcelData {
|
|||
@Pattern(regexp = "(^TEXT$)|(^STEP$)|(.{0})", message = "{test_case_step_model_validate}")
|
||||
private String stepModel;
|
||||
|
||||
@ColumnWidth(50)
|
||||
@ExcelProperty("Case status")
|
||||
private String status;
|
||||
|
||||
@ExcelProperty("Priority")
|
||||
@Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "{test_case_priority_validate}")
|
||||
private String priority;
|
||||
|
||||
@ExcelProperty("Maintainer(ID)")
|
||||
private String maintainer;
|
||||
|
||||
@Override
|
||||
public List<List<String>> getHead(boolean needNum, List<CustomFieldDao> customFields) {
|
||||
return super.getHead(needNum, customFields, Locale.US);
|
||||
|
|
|
@ -58,7 +58,7 @@ public class FunctionCaseTemplateWriteHandler implements RowWriteHandler {
|
|||
maintainerIndex = index;
|
||||
} else if (TestCaseImportFiled.PRIORITY.containsHead(head)) {
|
||||
priorityIndex = index;
|
||||
} else if (TestCaseImportFiled.TAG.containsHead(head)) {
|
||||
} else if (TestCaseImportFiled.TAGS.containsHead(head)) {
|
||||
tagIndex = index;
|
||||
} else if (TestCaseImportFiled.STATUS.containsHead(head)) {
|
||||
statusIndex = index;
|
||||
|
|
|
@ -8,13 +8,12 @@ import com.alibaba.fastjson.JSONObject;
|
|||
import io.metersphere.base.domain.TestCase;
|
||||
import io.metersphere.base.domain.TestCaseWithBLOBs;
|
||||
import io.metersphere.commons.constants.TestCaseConstants;
|
||||
import io.metersphere.commons.exception.CustomFieldValidateException;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.BeanUtils;
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.dto.CustomFieldDao;
|
||||
import io.metersphere.dto.CustomFieldOption;
|
||||
import io.metersphere.excel.annotation.NotRequired;
|
||||
import io.metersphere.excel.constants.TestCaseImportFiled;
|
||||
import io.metersphere.excel.domain.ExcelErrData;
|
||||
|
@ -26,6 +25,8 @@ import io.metersphere.excel.utils.FunctionCaseImportEnum;
|
|||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.track.request.testcase.TestCaseImportRequest;
|
||||
import io.metersphere.track.service.TestCaseService;
|
||||
import io.metersphere.track.validate.AbstractCustomFieldValidator;
|
||||
import io.metersphere.track.validate.CustomFieldValidatorFactory;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
@ -97,6 +98,8 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
|
|||
*/
|
||||
private HashMap<ExcelMergeInfo, String> mergeCellDataMap = new HashMap<>();
|
||||
|
||||
private HashMap<String, AbstractCustomFieldValidator> customFieldValidatorMap;
|
||||
|
||||
public boolean isUpdated() {
|
||||
return isUpdated;
|
||||
}
|
||||
|
@ -109,23 +112,11 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
|
|||
|
||||
this.request = request;
|
||||
|
||||
this.customFieldValidatorMap = CustomFieldValidatorFactory.getValidatorMap();
|
||||
|
||||
List<CustomFieldDao> customFields = request.getCustomFields();
|
||||
if (CollectionUtils.isNotEmpty(customFields)) {
|
||||
for (CustomFieldDao dto : customFields) {
|
||||
String name = dto.getName();
|
||||
if (StringUtils.isNotEmpty(name)) {
|
||||
name = name.trim();
|
||||
}
|
||||
if (TestCaseImportFiled.MAINTAINER.getFiledLangMap().values().contains(name)) {
|
||||
customFieldsMap.put("maintainer", dto);
|
||||
} else if (TestCaseImportFiled.PRIORITY.getFiledLangMap().values().contains(name)) {
|
||||
customFieldsMap.put("priority", dto);
|
||||
} else if (TestCaseImportFiled.STATUS.getFiledLangMap().values().contains(name)) {
|
||||
customFieldsMap.put("status", dto);
|
||||
} else {
|
||||
customFieldsMap.put(name, dto);
|
||||
}
|
||||
}
|
||||
customFieldsMap = customFields.stream().collect(Collectors.toMap(CustomFieldDao::getName, i -> i));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -377,48 +368,36 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
|
|||
return StringUtils.equals(request.getImportType(), FunctionCaseImportEnum.Create.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验自定义字段,并记录错误提示
|
||||
* 如果填写的是自定义字段的选项值,则转换成ID保存
|
||||
* @param data
|
||||
* @param stringBuilder
|
||||
*/
|
||||
private void validateCustomField(TestCaseExcelData data, StringBuilder stringBuilder) {
|
||||
//校验自定义必填字段
|
||||
for (Map.Entry<String, CustomFieldDao> customEntry : customFieldsMap.entrySet()) {
|
||||
String customName = customEntry.getKey();
|
||||
CustomFieldDao field = customEntry.getValue();
|
||||
|
||||
if (field.getRequired()) {
|
||||
String value;
|
||||
if (StringUtils.equals(customName, "status")) {
|
||||
Map<String, String> valueMap = new HashMap<>() {{
|
||||
put("Prepare", Translator.get("test_case_status_prepare"));
|
||||
put("Underway", Translator.get("test_case_status_running"));
|
||||
put("Completed", Translator.get("test_case_status_finished"));
|
||||
}};
|
||||
value = getCustomFieldValue(Translator.get("test_case_status"), data.getStatus(), field.getOptions(),
|
||||
stringBuilder, valueMap);
|
||||
data.setStatus(value);
|
||||
} else if (StringUtils.equals(customName, "priority")) {
|
||||
value = data.getPriority();
|
||||
} else if (StringUtils.equals(customName, "maintainer")) {
|
||||
value = data.getMaintainer();
|
||||
//校验维护人
|
||||
if (StringUtils.isBlank(value)) {
|
||||
data.setMaintainer(SessionUtils.getUserId());
|
||||
} else {
|
||||
if (!request.getUserIds().contains(value)) {
|
||||
stringBuilder.append(Translator.get("user_not_exists"))
|
||||
.append( ":")
|
||||
.append(value)
|
||||
.append(ERROR_MSG_SEPARATOR);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
value = data.getCustomDatas().get(customName);
|
||||
}
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
stringBuilder.append(field.getName())
|
||||
.append(" ")
|
||||
.append(Translator.get("required"))
|
||||
.append(ERROR_MSG_SEPARATOR);
|
||||
}
|
||||
Map<String, String> customData = data.getCustomData();
|
||||
for (String fieldName : customData.keySet()) {
|
||||
String value = customData.get(fieldName);
|
||||
CustomFieldDao customField = customFieldsMap.get(fieldName);
|
||||
if (customField == null) {
|
||||
continue;
|
||||
}
|
||||
AbstractCustomFieldValidator customFieldValidator = customFieldValidatorMap.get(customField.getType());
|
||||
try {
|
||||
customFieldValidator.validate(customField, value);
|
||||
} catch (CustomFieldValidateException e) {
|
||||
stringBuilder.append(e.getMessage().concat(ERROR_MSG_SEPARATOR));
|
||||
}
|
||||
if (customFieldValidator.isKVOption) {
|
||||
// 这里如果填的是选项值,替换成选项ID,保存
|
||||
customData.put(fieldName, customFieldValidator.parse2Key(value, customField));
|
||||
}
|
||||
if (StringUtils.equals(fieldName, TestCaseImportFiled.STATUS.getValue())) {
|
||||
data.setStatus(value);
|
||||
} else if (StringUtils.equals(fieldName, TestCaseImportFiled.PRIORITY.getValue())) {
|
||||
data.setPriority(value);
|
||||
} else if (StringUtils.equals(fieldName, TestCaseImportFiled.MAINTAINER.getValue())) {
|
||||
data.setMaintainer(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -485,37 +464,6 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据自定义字段的选项值获取对应的选项id
|
||||
*
|
||||
* @param name
|
||||
* @param text
|
||||
* @param optionsStr
|
||||
* @return
|
||||
*/
|
||||
public String getCustomFieldValue(String name, String text, String optionsStr, StringBuilder error,
|
||||
Map<String, String> systemValueMap) {
|
||||
List<String> textList = new ArrayList<>();
|
||||
if (StringUtils.isNotEmpty(optionsStr)) {
|
||||
List<CustomFieldOption> options = JSONObject.parseArray(optionsStr, CustomFieldOption.class);
|
||||
for (CustomFieldOption option : options) {
|
||||
// 系统字段需要翻译
|
||||
String i18nText = systemValueMap.get(option.getValue());
|
||||
if (i18nText != null) {
|
||||
option.setText(i18nText);
|
||||
}
|
||||
if (StringUtils.equals(option.getValue(), text) ||
|
||||
// 系统字段填写对应内容,而不是key的情况,比如用例状态填未开始而不是 Prepare
|
||||
StringUtils.equals(option.getText(), text)) {
|
||||
return option.getValue();
|
||||
}
|
||||
textList.add(option.getText());
|
||||
}
|
||||
}
|
||||
error.append(String.format(Translator.get("custom_field_option_not_exist"), name, textList.toString()));
|
||||
return text;
|
||||
}
|
||||
|
||||
public List<String> getNames() {
|
||||
return this.names;
|
||||
}
|
||||
|
@ -820,14 +768,8 @@ public class TestCaseNoModelDataListener extends AnalysisEventListener<Map<Integ
|
|||
data.setStepResult(value);
|
||||
} else if (StringUtils.equals(field, "stepModel")) {
|
||||
data.setStepModel(value);
|
||||
} else if (StringUtils.equals(field, "status")) {
|
||||
data.setStatus(value);
|
||||
} else if (StringUtils.equals(field, "maintainer")) {
|
||||
data.setMaintainer(value);
|
||||
} else if (StringUtils.equals(field, "priority")) {
|
||||
data.setPriority(value);
|
||||
} else {
|
||||
data.getCustomDatas().put(field, value);
|
||||
data.getCustomData().put(field, value);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package io.metersphere.track.constants;
|
||||
|
||||
public enum TestCaseStatus {
|
||||
Prepare("test_case_status_prepare"),
|
||||
Underway("test_case_status_prepare"),
|
||||
Completed("test_case_status_finished");
|
||||
|
||||
private String i18nKey;
|
||||
|
||||
TestCaseStatus(String i18nKey) {
|
||||
this.i18nKey = i18nKey;
|
||||
}
|
||||
|
||||
public String getI18nKey() {
|
||||
return i18nKey;
|
||||
}
|
||||
}
|
|
@ -1462,7 +1462,7 @@ public class TestCaseService {
|
|||
|
||||
for (TestCaseExcelData model : data) {
|
||||
List<Object> fields = new ArrayList<>();
|
||||
Map<String, String> customDataMaps = Optional.ofNullable(model.getCustomDatas())
|
||||
Map<String, String> customDataMaps = Optional.ofNullable(model.getCustomData())
|
||||
.orElse(new HashMap<>());
|
||||
Map<String, String> otherFieldMaps = Optional.ofNullable(model.getOtherFields())
|
||||
.orElse(new HashMap<>());
|
||||
|
@ -1626,7 +1626,7 @@ public class TestCaseService {
|
|||
map.put(customNameMap.get(id), value);
|
||||
}
|
||||
}
|
||||
data.setCustomDatas(map);
|
||||
data.setCustomData(map);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e);
|
||||
}
|
||||
|
|
|
@ -627,7 +627,7 @@ public class TestPlanService {
|
|||
Long nextScenarioOrder = ServiceUtils.getNextOrder(request.getPlanId(), extTestPlanScenarioCaseMapper::getLastOrder);
|
||||
|
||||
for (TestCaseTest l : list) {
|
||||
if (StringUtils.equals(l.getTestType(), TestCaseStatus.performance.name())) {
|
||||
if (StringUtils.equals(l.getTestType(), TestCaseTestStatus.performance.name())) {
|
||||
String id = l.getTestId();
|
||||
LoadTestWithBLOBs loadTest = loadTestMapper.selectByPrimaryKey(id);
|
||||
TestPlanLoadCaseWithBLOBs t = new TestPlanLoadCaseWithBLOBs();
|
||||
|
@ -650,7 +650,7 @@ public class TestPlanService {
|
|||
}
|
||||
|
||||
}
|
||||
if (StringUtils.equals(l.getTestType(), TestCaseStatus.testcase.name())) {
|
||||
if (StringUtils.equals(l.getTestType(), TestCaseTestStatus.testcase.name())) {
|
||||
TestPlanApiCase t = new TestPlanApiCase();
|
||||
ApiTestCaseWithBLOBs apitest = apiTestCaseMapper.selectByPrimaryKey(l.getTestId());
|
||||
if (null != apitest) {
|
||||
|
@ -674,7 +674,7 @@ public class TestPlanService {
|
|||
|
||||
|
||||
}
|
||||
if (StringUtils.equals(l.getTestType(), TestCaseStatus.automation.name())) {
|
||||
if (StringUtils.equals(l.getTestType(), TestCaseTestStatus.automation.name())) {
|
||||
TestPlanApiScenario t = new TestPlanApiScenario();
|
||||
ApiScenarioWithBLOBs testPlanApiScenario = apiScenarioMapper.selectByPrimaryKey(l.getTestId());
|
||||
if (testPlanApiScenario != null) {
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package io.metersphere.track.validate;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import io.metersphere.commons.exception.CustomFieldValidateException;
|
||||
import io.metersphere.dto.CustomFieldDao;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractCustomFieldValidator {
|
||||
|
||||
/**
|
||||
* 标记是否是键值对的选项
|
||||
* 需要校验时可以填键也可以填值
|
||||
*/
|
||||
public Boolean isKVOption = false;
|
||||
|
||||
/**
|
||||
* 校验参数是否合法
|
||||
* @param customField
|
||||
* @param value
|
||||
*/
|
||||
abstract public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException;
|
||||
|
||||
/**
|
||||
* 将选项的值转化为对应的key
|
||||
* @param keyOrValue
|
||||
* @return
|
||||
*/
|
||||
public String parse2Key(String keyOrValue, CustomFieldDao customField) {
|
||||
return keyOrValue;
|
||||
}
|
||||
|
||||
protected void validateRequired(CustomFieldDao customField, String value) throws CustomFieldValidateException {
|
||||
if (customField.getRequired() && StringUtils.isBlank(value)) {
|
||||
CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_required_tip"), customField.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
protected List<String> parse2Array(String name, String value) throws CustomFieldValidateException {
|
||||
try {
|
||||
return JSONArray.parseArray(value, String.class);
|
||||
} catch (Exception e) {
|
||||
CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_required_tip"), name));
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package io.metersphere.track.validate;
|
||||
|
||||
import io.metersphere.commons.exception.CustomFieldValidateException;
|
||||
import io.metersphere.commons.utils.DateUtils;
|
||||
import io.metersphere.dto.CustomFieldDao;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class CustomFieldDateTimeValidator extends AbstractCustomFieldValidator {
|
||||
|
||||
@Override
|
||||
public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException {
|
||||
validateRequired(customField, value);
|
||||
try {
|
||||
if (StringUtils.isNotBlank(value)) {
|
||||
DateUtils.getTime(value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_datetime_tip"), customField.getName(), DateUtils.TIME_PATTERN));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package io.metersphere.track.validate;
|
||||
|
||||
import io.metersphere.commons.exception.CustomFieldValidateException;
|
||||
import io.metersphere.commons.utils.DateUtils;
|
||||
import io.metersphere.dto.CustomFieldDao;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class CustomFieldDateValidator extends AbstractCustomFieldValidator {
|
||||
|
||||
public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException {
|
||||
validateRequired(customField, value);
|
||||
try {
|
||||
if (StringUtils.isNotBlank(value)) {
|
||||
DateUtils.getDate(value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_date_tip"), customField.getName(), DateUtils.DATE_PATTERM));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package io.metersphere.track.validate;
|
||||
|
||||
import io.metersphere.commons.exception.CustomFieldValidateException;
|
||||
import io.metersphere.dto.CustomFieldDao;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class CustomFieldFloatValidator extends AbstractCustomFieldValidator {
|
||||
|
||||
public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException {
|
||||
validateRequired(customField, value);
|
||||
try {
|
||||
if (StringUtils.isNotBlank(value)) {
|
||||
Float.parseFloat(value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_float_tip"), customField.getName()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package io.metersphere.track.validate;
|
||||
|
||||
import io.metersphere.commons.exception.CustomFieldValidateException;
|
||||
import io.metersphere.dto.CustomFieldDao;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class CustomFieldIntegerValidator extends AbstractCustomFieldValidator {
|
||||
|
||||
public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException {
|
||||
validateRequired(customField, value);
|
||||
try {
|
||||
if (StringUtils.isNotBlank(value)) {
|
||||
Integer.parseInt(value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_int_tip"), customField.getName()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package io.metersphere.track.validate;
|
||||
|
||||
import io.metersphere.base.domain.User;
|
||||
import io.metersphere.commons.exception.CustomFieldValidateException;
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.dto.CustomFieldDao;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.service.UserService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CustomFieldMemberValidator extends AbstractCustomFieldValidator {
|
||||
|
||||
protected Map<String, String> userIdMap;
|
||||
protected Map<String, String> userNameMap;
|
||||
|
||||
public CustomFieldMemberValidator() {
|
||||
this.isKVOption = true;
|
||||
UserService userService = CommonBeanFactory.getBean(UserService.class);
|
||||
List<User> memberOption = userService.getProjectMemberOption(SessionUtils.getCurrentProjectId());
|
||||
userIdMap = memberOption.stream().collect(Collectors.toMap(User::getId, User::getName));
|
||||
userNameMap = memberOption.stream().collect(Collectors.toMap(User::getName, User::getId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException {
|
||||
validateRequired(customField, value);
|
||||
if (StringUtils.isBlank(value)) {
|
||||
return;
|
||||
}
|
||||
if (userIdMap.containsKey(value) || userNameMap.containsKey(value)) {
|
||||
return;
|
||||
}
|
||||
throw new CustomFieldValidateException(String.format(Translator.get("custom_field_member_tip"), customField.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parse2Key(String keyOrValue, CustomFieldDao customField) {
|
||||
if (userNameMap.containsKey(keyOrValue)) {
|
||||
return userNameMap.get(keyOrValue);
|
||||
}
|
||||
return keyOrValue;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package io.metersphere.track.validate;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import io.metersphere.commons.exception.CustomFieldValidateException;
|
||||
import io.metersphere.dto.CustomFieldDao;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CustomFieldMultipleMemberValidator extends CustomFieldMemberValidator {
|
||||
|
||||
@Override
|
||||
public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException {
|
||||
validateRequired(customField, value);
|
||||
if (StringUtils.isBlank(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (String item : parse2Array(customField.getName(), value)) {
|
||||
if (!userIdMap.containsKey(item) && !userNameMap.containsKey(item)) {
|
||||
CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_member_tip"), customField.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parse2Key(String keyOrValuesStr, CustomFieldDao customField) {
|
||||
List<String> keyOrValues = JSONArray.parseArray(keyOrValuesStr, String.class);
|
||||
|
||||
for (int i = 0; i < keyOrValues.size(); i++) {
|
||||
String item = keyOrValues.get(i);
|
||||
if (userNameMap.containsKey(item)) {
|
||||
keyOrValues.set(i, userNameMap.get(item));
|
||||
}
|
||||
}
|
||||
return JSONArray.toJSONString(keyOrValues);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package io.metersphere.track.validate;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import io.metersphere.commons.exception.CustomFieldValidateException;
|
||||
import io.metersphere.dto.CustomFieldDao;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class CustomFieldMultipleSelectValidator extends CustomFieldSelectValidator {
|
||||
|
||||
@Override
|
||||
public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException {
|
||||
validateRequired(customField, value);
|
||||
if (StringUtils.isBlank(value)) {
|
||||
return;
|
||||
}
|
||||
Set<String> idSet = optionValueSetCache.get(customField.getId());
|
||||
Set<String> textSet = optionTextSetCache.get(customField.getId());
|
||||
for (String item : parse2Array(customField.getName(), value)) {
|
||||
if (!idSet.contains(item) && !textSet.contains(item)) {
|
||||
CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_select_tip"), customField.getName(), textSet));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parse2Key(String keyOrValuesStr, CustomFieldDao customField) {
|
||||
List<String> keyOrValues = JSONArray.parseArray(keyOrValuesStr, String.class);
|
||||
Map<String, String> nameMap = optionTextMapCache.get(customField.getId());
|
||||
for (int i = 0; i < keyOrValues.size(); i++) {
|
||||
String item = keyOrValues.get(i);
|
||||
if (nameMap.containsKey(item)) {
|
||||
keyOrValues.set(i, nameMap.get(item));
|
||||
}
|
||||
}
|
||||
return JSONArray.toJSONString(keyOrValues);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package io.metersphere.track.validate;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import io.metersphere.commons.exception.CustomFieldValidateException;
|
||||
import io.metersphere.dto.CustomFieldDao;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class CustomFieldMultipleTextValidator extends AbstractCustomFieldValidator {
|
||||
|
||||
public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException {
|
||||
validateRequired(customField, value);
|
||||
if (StringUtils.isNotBlank(value)) {
|
||||
try {
|
||||
JSONArray.parseArray(value);
|
||||
} catch (Exception e) {
|
||||
CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_array_tip"), customField.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
package io.metersphere.track.validate;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import io.metersphere.commons.exception.CustomFieldValidateException;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.dto.CustomFieldDao;
|
||||
import io.metersphere.dto.CustomFieldOption;
|
||||
import io.metersphere.excel.constants.TestCaseImportFiled;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.track.constants.TestCaseStatus;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CustomFieldSelectValidator extends AbstractCustomFieldValidator {
|
||||
|
||||
/**
|
||||
* 缓存每个字段对应的选项值
|
||||
*/
|
||||
Map<String, List<CustomFieldOption>> optionCache = new HashMap<>();
|
||||
Map<String, Set<String>> optionValueSetCache = new HashMap<>();
|
||||
Map<String, Set<String>> optionTextSetCache = new HashMap<>();
|
||||
Map<String, Map<String, String>> optionTextMapCache = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 保存系统字段中选项翻译后的值
|
||||
* key 为字段名称,value 为选项value,和选项值的映射
|
||||
*/
|
||||
Map<String, Map<String, String>> i18nMap = new HashMap<>();
|
||||
|
||||
public CustomFieldSelectValidator() {
|
||||
Map<String, String> statusI18nMap = new HashMap<>();
|
||||
for (TestCaseStatus status : TestCaseStatus.values()) {
|
||||
statusI18nMap.put(status.name(), Translator.get(status.getI18nKey()));
|
||||
}
|
||||
i18nMap.put(TestCaseImportFiled.STATUS.getFiledLangMap().get(Locale.SIMPLIFIED_CHINESE), statusI18nMap);
|
||||
this.isKVOption = true;
|
||||
}
|
||||
|
||||
public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException {
|
||||
validateRequired(customField, value);
|
||||
if (StringUtils.isBlank(value)) {
|
||||
return;
|
||||
}
|
||||
prepareCatch(customField);
|
||||
Set<String> idSet = optionValueSetCache.get(customField.getId());
|
||||
Set<String> textSet = optionTextSetCache.get(customField.getId());
|
||||
if (!idSet.contains(value) && !textSet.contains(value)) {
|
||||
CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_select_tip"), customField.getName(), textSet));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parse2Key(String keyOrValuesStr, CustomFieldDao customField) {
|
||||
Map<String, String> textMap = optionTextMapCache.get(customField.getId());
|
||||
if (MapUtils.isNotEmpty(textMap) && textMap.containsKey(keyOrValuesStr)) {
|
||||
return textMap.get(keyOrValuesStr);
|
||||
}
|
||||
return keyOrValuesStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义字段的选项值和key
|
||||
* 存储到缓存中,增强导入时性能
|
||||
*
|
||||
* @param customField
|
||||
*/
|
||||
private void prepareCatch(CustomFieldDao customField) {
|
||||
if (optionValueSetCache.get(customField.getId()) == null) {
|
||||
List<CustomFieldOption> options = getOptions(customField.getId(), customField.getOptions());
|
||||
|
||||
translateSystemOption(customField, options);
|
||||
|
||||
optionValueSetCache.put(customField.getId(), getIdSet(options));
|
||||
optionTextSetCache.put(customField.getId(), getNameSet(options));
|
||||
optionTextMapCache.put(customField.getId(), getTextMap(options));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译系统字段的选项名称
|
||||
* @param customField
|
||||
* @param options
|
||||
*/
|
||||
private void translateSystemOption(CustomFieldDao customField, List<CustomFieldOption> options) {
|
||||
Map<String, String> fieldI18nMap = i18nMap.get(customField.getName());
|
||||
if (fieldI18nMap != null) {
|
||||
// 不为空,说明需要翻译
|
||||
Iterator<CustomFieldOption> iterator = options.iterator();
|
||||
// 先将系统字段删掉
|
||||
while (iterator.hasNext()) {
|
||||
CustomFieldOption option = iterator.next();
|
||||
if (option.getSystem()) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
// 再填充翻译后的值
|
||||
for (String optionValue : fieldI18nMap.keySet()) {
|
||||
CustomFieldOption option = new CustomFieldOption(optionValue, fieldI18nMap.get(optionValue), true);
|
||||
options.add(option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Map<String, String> getTextMap(List<CustomFieldOption> options) {
|
||||
return options.stream()
|
||||
.collect(Collectors.toMap(CustomFieldOption::getValue, CustomFieldOption::getText));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected Set<String> getNameSet(List<CustomFieldOption> options) {
|
||||
return options.stream()
|
||||
.map(CustomFieldOption::getText)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected Set<String> getIdSet(List<CustomFieldOption> options) {
|
||||
return options.stream()
|
||||
.map(CustomFieldOption::getValue)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
protected List<CustomFieldOption> getOptions(String id, String optionsStr) {
|
||||
List<CustomFieldOption> options = optionCache.get(id);
|
||||
if (options != null) {
|
||||
return options;
|
||||
}
|
||||
try {
|
||||
return JSONArray.parseArray(optionsStr, CustomFieldOption.class);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e);
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package io.metersphere.track.validate;
|
||||
|
||||
import io.metersphere.commons.exception.CustomFieldValidateException;
|
||||
import io.metersphere.dto.CustomFieldDao;
|
||||
|
||||
public class CustomFieldTextValidator extends AbstractCustomFieldValidator {
|
||||
|
||||
public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException {
|
||||
validateRequired(customField, value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package io.metersphere.track.validate;
|
||||
|
||||
import io.metersphere.commons.constants.CustomFieldType;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class CustomFieldValidatorFactory {
|
||||
|
||||
public static HashMap<String, AbstractCustomFieldValidator> getValidatorMap() {
|
||||
return new HashMap<>() {{
|
||||
put(CustomFieldType.SELECT.getValue(), new CustomFieldSelectValidator());
|
||||
put(CustomFieldType.RADIO.getValue(), new CustomFieldSelectValidator());
|
||||
|
||||
put(CustomFieldType.MULTIPLE_SELECT.getValue(), new CustomFieldMultipleSelectValidator());
|
||||
put(CustomFieldType.CHECKBOX.getValue(), new CustomFieldMultipleSelectValidator());
|
||||
|
||||
put(CustomFieldType.INPUT.getValue(), new CustomFieldTextValidator());
|
||||
put(CustomFieldType.RICH_TEXT.getValue(), new CustomFieldTextValidator());
|
||||
put(CustomFieldType.TEXTAREA.getValue(), new CustomFieldTextValidator());
|
||||
|
||||
put(CustomFieldType.MULTIPLE_INPUT.getValue(), new CustomFieldMultipleTextValidator());
|
||||
|
||||
put(CustomFieldType.DATE.getValue(), new CustomFieldDateValidator());
|
||||
put(CustomFieldType.DATETIME.getValue(), new CustomFieldDateTimeValidator());
|
||||
|
||||
put(CustomFieldType.MEMBER.getValue(), new CustomFieldMemberValidator());
|
||||
put(CustomFieldType.MULTIPLE_MEMBER.getValue(), new CustomFieldMultipleMemberValidator());
|
||||
|
||||
put(CustomFieldType.INT.getValue(), new CustomFieldIntegerValidator());
|
||||
put(CustomFieldType.FLOAT.getValue(), new CustomFieldFloatValidator());
|
||||
}};
|
||||
}
|
||||
}
|
|
@ -323,6 +323,14 @@ test_case_plan_comment=执行
|
|||
test_case_review_comment=评审
|
||||
plan_case_status_blocking=阻塞
|
||||
plan_case_status_skip=跳过
|
||||
custom_field_required_tip=[%s]为必填项
|
||||
custom_field_array_tip=[%s]必须是数组
|
||||
custom_field_datetime_tip=[%s]必须为时间日期格式[%s]
|
||||
custom_field_date_tip=[%s]必须为日期格式[%s]
|
||||
custom_field_float_tip=[%s]必须为数字
|
||||
custom_field_int_tip=[%s]必须为整型
|
||||
custom_field_member_tip=[%s]必须当前项目成员
|
||||
custom_field_select_tip=[%s]必须为%s
|
||||
# mock
|
||||
mock_warning=未找到匹配的Mock期望
|
||||
zentao_test_type_error=无效的 Zentao 请求
|
||||
|
|
Loading…
Reference in New Issue