This commit is contained in:
fit2-zhao 2020-09-18 11:41:25 +08:00
commit c5d52a6906
22 changed files with 125 additions and 83 deletions

View File

@ -15,7 +15,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<shiro.version>1.5.1</shiro.version>
<shiro.version>1.6.0</shiro.version>
<java.version>1.8</java.version>
<jmeter.version>5.2.1</jmeter.version>
<nacos.version>1.1.3</nacos.version>

View File

@ -9,10 +9,7 @@ import io.metersphere.commons.utils.LogUtil;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jetty.http.HttpHeader;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@ -21,10 +18,8 @@ import java.util.Optional;
public abstract class ApiImportAbstractParser implements ApiImportParser {
protected String getApiTestStr(InputStream source) {
BufferedReader bufferedReader;
StringBuilder testStr = null;
try {
bufferedReader = new BufferedReader(new InputStreamReader(source, StandardCharsets.UTF_8));
try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(source, StandardCharsets.UTF_8))) {
testStr = new StringBuilder();
String inputStr;
while ((inputStr = bufferedReader.readLine()) != null) {

View File

@ -3,6 +3,7 @@ package io.metersphere.api.parse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.parse.ApiImport;
import io.metersphere.api.dto.scenario.request.RequestType;
@ -23,7 +24,7 @@ public class MsParser extends ApiImportAbstractParser {
}
private String parsePluginFormat(String testStr) {
JSONObject testObject = JSONObject.parseObject(testStr);
JSONObject testObject = JSONObject.parseObject(testStr, Feature.OrderedField);
if (testObject.get("scenarios") != null) {
return testStr;
} else {

View File

@ -159,32 +159,34 @@ public class Swagger2Parser extends ApiImportAbstractParser {
private JSONObject getBodyJSONObjectParameters(Map<String, Property> properties, Map<String, Model> definitions, HashSet<String> refSet) {
JSONObject jsonObject = new JSONObject();
properties.forEach((key, value) -> {
if (value instanceof ObjectProperty) {
ObjectProperty objectProperty = (ObjectProperty) value;
jsonObject.put(key, getBodyJSONObjectParameters(objectProperty.getProperties(), definitions, refSet));
} else if (value instanceof ArrayProperty) {
ArrayProperty arrayProperty = (ArrayProperty) value;
Property items = arrayProperty.getItems();
if (items instanceof RefProperty) {
RefProperty refProperty = (RefProperty) items;
String simpleRef = refProperty.getSimpleRef();
if (refSet.contains(simpleRef)) {
jsonObject.put(key, new JSONArray());
return;
if (properties != null) {
properties.forEach((key, value) -> {
if (value instanceof ObjectProperty) {
ObjectProperty objectProperty = (ObjectProperty) value;
jsonObject.put(key, getBodyJSONObjectParameters(objectProperty.getProperties(), definitions, refSet));
} else if (value instanceof ArrayProperty) {
ArrayProperty arrayProperty = (ArrayProperty) value;
Property items = arrayProperty.getItems();
if (items instanceof RefProperty) {
RefProperty refProperty = (RefProperty) items;
String simpleRef = refProperty.getSimpleRef();
if (refSet.contains(simpleRef)) {
jsonObject.put(key, new JSONArray());
return;
}
refSet.add(simpleRef);
Model model = definitions.get(simpleRef);
JSONArray propertyList = new JSONArray();
propertyList.add(getBodyJSONObjectParameters(model.getProperties(), definitions, refSet));
jsonObject.put(key, propertyList);
} else {
jsonObject.put(key, new ArrayList<>());
}
refSet.add(simpleRef);
Model model = definitions.get(simpleRef);
JSONArray propertyList = new JSONArray();
propertyList.add(getBodyJSONObjectParameters(model.getProperties(), definitions, refSet));
jsonObject.put(key, propertyList);
} else {
jsonObject.put(key, new ArrayList<>());
jsonObject.put(key, Optional.ofNullable(value.getDescription()).orElse(""));
}
} else {
jsonObject.put(key, Optional.ofNullable(value.getDescription()).orElse(""));
}
});
});
}
return jsonObject;
}

View File

@ -10,7 +10,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
public class KafkaProperties {
public static final String KAFKA_PREFIX = "kafka";
private String acks;
private String acks = "all";
private String topic;
private String fields;
private String timestamp;

View File

@ -17,7 +17,7 @@ import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
public abstract class EasyExcelListener<T> extends AnalysisEventListener<T> {
public abstract class EasyExcelListener<T> extends AnalysisEventListener<T> implements AutoCloseable {
protected List<ExcelErrData<T>> errList = new ArrayList<>();
@ -153,6 +153,8 @@ public abstract class EasyExcelListener<T> extends AnalysisEventListener<T> {
return errList;
}
@Override
public void close() {
this.easyExcelI18nTranslator.resetExcelProperty();
}

View File

@ -5,7 +5,6 @@ import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.exception.ExcelException;
import org.apache.poi.ss.usermodel.IndexedColors;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@ -13,7 +12,7 @@ import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
public class EasyExcelExporter {
public class EasyExcelExporter implements AutoCloseable {
EasyExcelI18nTranslator easyExcelI18nTranslator;
@ -47,6 +46,7 @@ public class EasyExcelExporter {
}
}
@Override
public void close() {
easyExcelI18nTranslator.resetExcelProperty();
}

View File

@ -81,8 +81,8 @@ public class TestPlanController {
@PostMapping("/edit")
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public void editTestPlan(@RequestBody TestPlan testPlan) {
testPlanService.editTestPlan(testPlan);
public void editTestPlan(@RequestBody TestPlanDTO testPlanDTO) {
testPlanService.editTestPlan(testPlanDTO);
}
@PostMapping("/edit/status/{planId}")

View File

@ -4,8 +4,11 @@ import io.metersphere.base.domain.TestPlan;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class TestPlanDTO extends TestPlan {
private String projectName;
private List<String> projectIds;
}

View File

@ -273,16 +273,12 @@ public class TestCaseService {
Set<String> userIds = userRoleMapper.selectByExample(userRoleExample).stream().map(UserRole::getUserId).collect(Collectors.toSet());
EasyExcelListener easyExcelListener = null;
try {
easyExcelListener = new TestCaseDataListener(this, projectId, testCaseNames, userIds);
try (EasyExcelListener easyExcelListener = new TestCaseDataListener(this, projectId, testCaseNames, userIds)) {
EasyExcelFactory.read(multipartFile.getInputStream(), TestCaseExcelData.class, easyExcelListener).sheet().doRead();
errList = easyExcelListener.getErrList();
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
MSException.throwException(e.getMessage());
} finally {
easyExcelListener.close();
}
}
@ -316,15 +312,11 @@ public class TestCaseService {
}
public void testCaseTemplateExport(HttpServletResponse response) {
EasyExcelExporter easyExcelExporter = null;
try {
easyExcelExporter = new EasyExcelExporter(TestCaseExcelData.class);
try (EasyExcelExporter easyExcelExporter = new EasyExcelExporter(TestCaseExcelData.class)) {
easyExcelExporter.export(response, generateExportTemplate(),
Translator.get("test_case_import_template_name"), Translator.get("test_case_import_template_sheet"));
} catch (Exception e) {
MSException.throwException(e);
} finally {
easyExcelExporter.close();
}
}
@ -398,15 +390,11 @@ public class TestCaseService {
}
public void testCaseExport(HttpServletResponse response, TestCaseBatchRequest request) {
EasyExcelExporter easyExcelExporter = null;
try {
easyExcelExporter = new EasyExcelExporter(TestCaseExcelData.class);
try (EasyExcelExporter easyExcelExporter = new EasyExcelExporter(TestCaseExcelData.class)) {
easyExcelExporter.export(response, generateTestCaseExcel(request),
Translator.get("test_case_import_template_name"), Translator.get("test_case_import_template_sheet"));
} catch (Exception e) {
MSException.throwException(e);
} finally {
easyExcelExporter.close();
}
}

View File

@ -34,6 +34,7 @@ import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.*;
@ -110,12 +111,51 @@ public class TestPlanService {
return Optional.ofNullable(testPlanMapper.selectByPrimaryKey(testPlanId)).orElse(new TestPlan());
}
public int editTestPlan(TestPlan testPlan) {
public int editTestPlan(TestPlanDTO testPlan) {
editTestPlanProject(testPlan);
testPlan.setUpdateTime(System.currentTimeMillis());
checkTestPlanExist(testPlan);
return testPlanMapper.updateByPrimaryKeySelective(testPlan);
}
private void editTestPlanProject(TestPlanDTO testPlan) {
List<String> projectIds = testPlan.getProjectIds();
if (!CollectionUtils.isEmpty(projectIds)) {
TestPlanProjectExample testPlanProjectExample1 = new TestPlanProjectExample();
testPlanProjectExample1.createCriteria().andTestPlanIdEqualTo(testPlan.getId());
List<TestPlanProject> testPlanProjects = testPlanProjectMapper.selectByExample(testPlanProjectExample1);
// 已经关联的项目idList
List<String> dbProjectIds = testPlanProjects.stream().map(TestPlanProject::getProjectId).collect(Collectors.toList());
// 修改后传过来的项目idList如果还未关联进行关联
projectIds.forEach(projectId -> {
if (!dbProjectIds.contains(projectId)) {
TestPlanProject testPlanProject = new TestPlanProject();
testPlanProject.setTestPlanId(testPlan.getId());
testPlanProject.setProjectId(projectId);
testPlanProjectMapper.insert(testPlanProject);
}
});
TestPlanProjectExample testPlanProjectExample = new TestPlanProjectExample();
testPlanProjectExample.createCriteria().andTestPlanIdEqualTo(testPlan.getId()).andProjectIdNotIn(projectIds);
testPlanProjectMapper.deleteByExample(testPlanProjectExample);
// 关联的项目下的用例idList
TestCaseExample example = new TestCaseExample();
example.createCriteria().andProjectIdIn(projectIds);
List<TestCase> caseList = testCaseMapper.selectByExample(example);
List<String> caseIds = caseList.stream().map(TestCase::getId).collect(Collectors.toList());
// 取消关联所属项目下的用例和计划的关系
TestPlanTestCaseExample testPlanTestCaseExample = new TestPlanTestCaseExample();
TestPlanTestCaseExample.Criteria criteria = testPlanTestCaseExample.createCriteria().andPlanIdEqualTo(testPlan.getId());
if (!CollectionUtils.isEmpty(caseIds)) {
criteria.andCaseIdNotIn(caseIds);
}
testPlanTestCaseMapper.deleteByExample(testPlanTestCaseExample);
}
}
private void checkTestPlanExist(TestPlan testPlan) {
if (testPlan.getName() != null) {
TestPlanExample example = new TestPlanExample();

@ -1 +1 @@
Subproject commit d5b4969642fd8d10cc2f949d7377e0a0e5217a3a
Subproject commit 321c869938357e8c2253e5bd86c963828664ae23

View File

@ -40,7 +40,6 @@ spring.flyway.validate-on-migrate=false
spring.messages.basename=i18n/messages
# kafka
kafka.acks=1
kafka.fields=
kafka.timestamp=yyyy-MM-dd'T'HH:mm:ss.SSSZZ
kafka.sample-filter=

View File

@ -57,7 +57,7 @@
<el-tab-pane :label="$t('api_test.request.headers')" name="headers">
<ms-api-key-value :is-read-only="isReadOnly" :isShowEnable="true" :suggestions="headerSuggestions" :items="request.headers"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.body')" name="body" v-if="isNotGet">
<el-tab-pane :label="$t('api_test.request.body')" name="body">
<ms-api-body :is-read-only="isReadOnly"
:body="request.body"
:scenario="scenario"
@ -190,9 +190,6 @@ export default {
},
computed: {
isNotGet() {
return this.request.method !== "GET";
},
displayUrl() {
return (this.request.environment && this.request.environment.config.httpConfig.socket) ?
this.request.environment.config.httpConfig.protocol + '://' + this.request.environment.config.httpConfig.socket + (this.request.path ? this.request.path : '')

View File

@ -4,7 +4,7 @@
<el-button size="mini" @click="openController"
class="condition"
:class="controllerClass"
v-if="request.controller.isValid()">
v-if="request.controller && request.controller.isValid()">
<el-row type="flex" align="middle">
<font-awesome-icon :icon="['fas', 'random']"/>
<div class="condition-label">{{ request.controller.label() }}</div>
@ -16,10 +16,10 @@
<el-button size="mini" @click="openTimer"
class="condition"
:class="timerClass"
v-if="request.timer.isValid()">
v-if="request.timer && request.timer.isValid()">
<el-row type="flex" align="middle">
<font-awesome-icon :icon="['fas', 'clock']"/>
<div class="condition-label">{{ request.timer.label() }}</div>
<div class="condition-label">{{ request.timer && request.timer.label() }}</div>
</el-row>
</el-button>
</div>

View File

@ -1065,11 +1065,8 @@ class JMXGenerator {
} else if (request instanceof HttpRequest) {
sampler = new HTTPSamplerProxy(request.name || "", new JMXHttpRequest(request, scenario.environment));
this.addRequestHeader(sampler, request);
if (request.method.toUpperCase() === 'GET') {
this.addRequestArguments(sampler, request);
} else {
this.addRequestBody(sampler, request, testId);
}
this.addRequestArguments(sampler, request);
this.addRequestBody(sampler, request, testId);
} else if (request instanceof SqlRequest) {
request.dataSource = scenario.databaseConfigMap.get(request.dataSource);
sampler = new JDBCSampler(request.name || "", request);

View File

@ -342,6 +342,7 @@
{min: 2, max: 50, message: this.$t('commons.input_limit', [2, 50]), trigger: 'blur'},
{
required: true,
pattern: '^[^\u4e00-\u9fa5]+$',
message: this.$t('user.special_characters_are_not_supported'),
trigger: 'blur'
}

View File

@ -4,7 +4,7 @@
:close-on-click-modal="false"
class="ms-switch-project"
>
<ms-table-header :condition.sync="condition" @search="initData" title="" :show-create="false"/>
<ms-table-header :condition.sync="condition" @search="initData" title="切换项目" :show-create="false"/>
<el-table
:data="tableData"
highlight-current-row

View File

@ -24,7 +24,6 @@
<el-col :span="11" :offset="2">
<el-form-item :label="$t('test_track.plan.plan_project')" :label-width="formLabelWidth" prop="projectIds">
<el-select
:disabled="(form.status == null) ? false : true"
v-model="form.projectIds"
:placeholder="$t('test_track.plan.input_plan_project')"
multiple
@ -175,18 +174,34 @@ export default {
return;
}
param.workspaceId = localStorage.getItem(WORKSPACE_ID);
this.$post('/test/plan/' + this.operationType, param, () => {
this.$success(this.$t('commons.save_success'));
this.dialogFormVisible = false;
this.$emit("refresh");
// 广 head
TrackEvent.$emit(LIST_CHANGE);
});
if (this.operationType === 'edit') {
this.$confirm('取消项目关联会同时取消该项目下已关联的测试用例', '提示', {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
this.editTestPlan(param);
}).catch(() => {
this.$info(this.$t('commons.cancel'))
});
} else {
this.editTestPlan(param);
}
} else {
return false;
}
});
},
editTestPlan(param) {
this.$post('/test/plan/' + this.operationType, param, () => {
this.$success(this.$t('commons.save_success'));
this.dialogFormVisible = false;
this.$emit("refresh");
// 广 head
TrackEvent.$emit(LIST_CHANGE);
});
},
getProjects() {
this.$get("/project/listAll", (response) => {
if (response.success) {

View File

@ -490,14 +490,14 @@
if (this.issuesSwitch) {
let desc = this.addPLabel('[' + this.$t('test_track.plan_view.operate_step') + ']');
let result = this.addPLabel('[' + this.$t('test_track.case.expected_results') + ']');
let executeResult = this.addPLabel('[' + this.$t('test_track.plan_view.actual_result') + ']');
let actualResult = this.addPLabel('[' + this.$t('test_track.plan_view.actual_result') + ']');
this.testCase.steps.forEach(step => {
let stepPrefix = this.$t('test_track.plan_view.step') + step.num + ':';
desc += this.addPLabel(stepPrefix + (step.desc == undefined ? '' : step.desc));
result += this.addPLabel(stepPrefix + (step.result == undefined ? '' : step.result));
executeResult += this.addPLabel(stepPrefix + (step.executeResult == undefined ? '' : step.executeResult));
actualResult += this.addPLabel(stepPrefix + (step.actualResult == undefined ? '' : step.actualResult));
});
this.testCase.issues.content = desc + this.addPLabel('') + result + this.addPLabel('') + executeResult + this.addPLabel('');
this.testCase.issues.content = desc + this.addPLabel('') + result + this.addPLabel('') + actualResult + this.addPLabel('');
this.$get("/test/case/project/" + this.testCase.caseId, res => {
const project = res.data;

View File

@ -6,6 +6,7 @@
:title="operationType === 'edit' ? '编辑用例评审' : '创建用例评审'"
:visible.sync="dialogFormVisible"
@close="close"
v-loading="result.loading"
width="65%">
<el-form :model="form" :rules="rules" ref="reviewForm">
@ -122,6 +123,7 @@ export default {
data() {
return {
dialogFormVisible: false,
result: {},
form: {
name: '',
projectIds: [],
@ -187,7 +189,7 @@ export default {
});
},
getProjects() {
this.$get("/project/listAll", (response) => {
this.result = this.$get("/project/listAll", (response) => {
if (response.success) {
this.projects = response.data;
} else {
@ -197,7 +199,7 @@ export default {
},
setReviewerOptions() {
let workspaceId = localStorage.getItem(WORKSPACE_ID);
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
this.result = this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
this.reviewerOptions = response.data;
});
},

@ -1 +1 @@
Subproject commit 0a375848d034d20eaf05caf11769e1c75c39235c
Subproject commit f2d5a342c82e629f510550d5778d752bb73bf5e7