Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
9f33d88d3a
|
@ -422,6 +422,12 @@
|
|||
<version>${jmeter.version}</version>
|
||||
</dependency>
|
||||
<!-- 添加jmeter包支持导入的jmx能正常执行 -->
|
||||
<dependency>
|
||||
<groupId>com.jayway.jsonpath</groupId>
|
||||
<artifactId>json-path</artifactId>
|
||||
<version>2.5.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -34,4 +34,14 @@ public class SaveApiPlanRequest {
|
|||
|
||||
private String projectId;
|
||||
|
||||
/**
|
||||
* 项目环境对应关系
|
||||
*/
|
||||
private Map<String, String> envMap;
|
||||
|
||||
/**
|
||||
* 用例的环境的对应关系
|
||||
*/
|
||||
private Map<String, List<String>> mapping;
|
||||
|
||||
}
|
||||
|
|
|
@ -489,6 +489,7 @@ public class MsJmeterParser extends ApiImportAbstractParser<ScenarioImport> {
|
|||
assertionJsonPath.setDescription(jsonPathAssertion.getName());
|
||||
assertionJsonPath.setExpression(jsonPathAssertion.getJsonPath());
|
||||
assertionJsonPath.setExpect(jsonPathAssertion.getExpectedValue());
|
||||
assertionJsonPath.setOption(jsonPathAssertion.getPropertyAsString("ASS_OPTION"));
|
||||
assertions.setName(jsonPathAssertion.getName());
|
||||
assertions.getJsonPath().add(assertionJsonPath);
|
||||
} else if (key instanceof XPath2Assertion) {
|
||||
|
|
|
@ -10,6 +10,7 @@ public class MsAssertionJsonPath extends MsAssertionType {
|
|||
private String expect;
|
||||
private String expression;
|
||||
private String description;
|
||||
private String option = "REGEX";
|
||||
|
||||
public MsAssertionJsonPath() {
|
||||
setType(MsAssertionType.JSON_PATH);
|
||||
|
|
|
@ -96,7 +96,12 @@ public class MsAssertions extends MsTestElement {
|
|||
assertion.setJsonValidationBool(true);
|
||||
assertion.setExpectNull(false);
|
||||
assertion.setInvert(false);
|
||||
assertion.setProperty("ASS_OPTION",assertionJsonPath.getOption());
|
||||
if (StringUtils.isEmpty(assertionJsonPath.getOption()) || "REGEX".equals(assertionJsonPath.getOption())) {
|
||||
assertion.setIsRegex(true);
|
||||
} else {
|
||||
assertion.setIsRegex(false);
|
||||
}
|
||||
return assertion;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,8 @@ public class MsJSR223Processor extends MsTestElement {
|
|||
if (StringUtils.isNotEmpty(name) && !config.isOperating()) {
|
||||
processor.setName(this.getName() + "<->" + name);
|
||||
}
|
||||
processor.setProperty("MS-ID", this.getId());
|
||||
|
||||
processor.setProperty(TestElement.TEST_CLASS, JSR223Sampler.class.getName());
|
||||
processor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
|
||||
processor.setProperty("cacheKey", "true");
|
||||
|
|
|
@ -82,6 +82,7 @@ public class MsDubboSampler extends MsTestElement {
|
|||
}
|
||||
sampler.setProperty(TestElement.TEST_CLASS, DubboSample.class.getName());
|
||||
sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("DubboSampleGui"));
|
||||
sampler.setProperty("MS-ID", this.getId());
|
||||
|
||||
sampler.addTestElement(configCenter(this.getConfigCenter()));
|
||||
sampler.addTestElement(registryCenter(this.getRegistryCenter()));
|
||||
|
|
|
@ -106,6 +106,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
}
|
||||
sampler.setProperty(TestElement.TEST_CLASS, HTTPSamplerProxy.class.getName());
|
||||
sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("HttpTestSampleGui"));
|
||||
sampler.setProperty("MS-ID", this.getId());
|
||||
sampler.setMethod(this.getMethod());
|
||||
sampler.setContentEncoding("UTF-8");
|
||||
sampler.setConnectTimeout(this.getConnectTimeout() == null ? "6000" : this.getConnectTimeout());
|
||||
|
|
|
@ -119,6 +119,8 @@ public class MsJDBCSampler extends MsTestElement {
|
|||
}
|
||||
sampler.setProperty(TestElement.TEST_CLASS, JDBCSampler.class.getName());
|
||||
sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
|
||||
sampler.setProperty("MS-ID", this.getId());
|
||||
|
||||
// request.getDataSource() 是ID,需要转换为Name
|
||||
sampler.setProperty("dataSource", this.dataSource.getName());
|
||||
sampler.setProperty("query", this.getQuery());
|
||||
|
|
|
@ -117,6 +117,7 @@ public class MsTCPSampler extends MsTestElement {
|
|||
if (StringUtils.isNotEmpty(name) && !config.isOperating()) {
|
||||
tcpSampler.setName(this.getName() + "<->" + name);
|
||||
}
|
||||
tcpSampler.setProperty("MS-ID", this.getId());
|
||||
|
||||
tcpSampler.setProperty(TestElement.TEST_CLASS, TCPSampler.class.getName());
|
||||
tcpSampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TCPSamplerGui"));
|
||||
|
|
|
@ -303,6 +303,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
|
|||
|
||||
private RequestResult getRequestResult(SampleResult result) {
|
||||
RequestResult requestResult = new RequestResult();
|
||||
requestResult.setId(result.getSamplerId());
|
||||
requestResult.setName(result.getSampleLabel());
|
||||
requestResult.setUrl(result.getUrlAsString());
|
||||
requestResult.setMethod(getMethod(result));
|
||||
|
|
|
@ -7,6 +7,8 @@ import java.util.List;
|
|||
|
||||
@Data
|
||||
public class RequestResult {
|
||||
// 请求ID
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
|
|
|
@ -611,27 +611,38 @@ public class ApiAutomationService {
|
|||
if (CollectionUtils.isEmpty(request.getPlanIds())) {
|
||||
MSException.throwException(Translator.get("plan id is null "));
|
||||
}
|
||||
List<String> scenarioIds = request.getScenarioIds();
|
||||
if (request.isSelectAllDate()) {
|
||||
scenarioIds = this.getAllScenarioIdsByFontedSelect(
|
||||
request.getModuleIds(), request.getName(), request.getProjectId(), request.getFilters(), request.getUnSelectIds());
|
||||
}
|
||||
// List<String> scenarioIds = request.getScenarioIds();
|
||||
// if (request.isSelectAllDate()) {
|
||||
// scenarioIds = this.getAllScenarioIdsByFontedSelect(
|
||||
// request.getModuleIds(), request.getName(), request.getProjectId(), request.getFilters(), request.getUnSelectIds());
|
||||
// }
|
||||
Map<String, List<String>> mapping = request.getMapping();
|
||||
Map<String, String> envMap = request.getEnvMap();
|
||||
Set<String> set = mapping.keySet();
|
||||
List<TestPlanDTO> list = extTestPlanMapper.selectByIds(request.getPlanIds());
|
||||
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||
ExtTestPlanScenarioCaseMapper scenarioBatchMapper = sqlSession.getMapper(ExtTestPlanScenarioCaseMapper.class);
|
||||
ExtTestPlanApiCaseMapper apiCaseBatchMapper = sqlSession.getMapper(ExtTestPlanApiCaseMapper.class);
|
||||
|
||||
for (TestPlanDTO testPlan : list) {
|
||||
if (scenarioIds != null) {
|
||||
for (String scenarioId : scenarioIds) {
|
||||
if (!set.isEmpty()) {
|
||||
set.forEach(id -> {
|
||||
Map<String, String> newEnvMap = new HashMap<>(16);
|
||||
if (envMap != null && !envMap.isEmpty()) {
|
||||
List<String> lt = mapping.get(id);
|
||||
lt.forEach(l -> {
|
||||
newEnvMap.put(l, envMap.get(l));
|
||||
});
|
||||
}
|
||||
TestPlanApiScenario testPlanApiScenario = new TestPlanApiScenario();
|
||||
testPlanApiScenario.setId(UUID.randomUUID().toString());
|
||||
testPlanApiScenario.setApiScenarioId(scenarioId);
|
||||
testPlanApiScenario.setApiScenarioId(id);
|
||||
testPlanApiScenario.setTestPlanId(testPlan.getId());
|
||||
testPlanApiScenario.setCreateTime(System.currentTimeMillis());
|
||||
testPlanApiScenario.setUpdateTime(System.currentTimeMillis());
|
||||
testPlanApiScenario.setEnvironment(JSON.toJSONString(newEnvMap));
|
||||
scenarioBatchMapper.insertIfNotExists(testPlanApiScenario);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (request.getApiIds() != null) {
|
||||
for (String caseId : request.getApiIds()) {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package io.metersphere.base.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApiDefinition implements Serializable {
|
||||
|
@ -38,5 +37,7 @@ public class ApiDefinition implements Serializable {
|
|||
|
||||
private String tags;
|
||||
|
||||
private String originalState;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
|
@ -1193,6 +1193,76 @@ public class ApiDefinitionExample {
|
|||
addCriterion("tags not between", value1, value2, "tags");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateIsNull() {
|
||||
addCriterion("original_state is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateIsNotNull() {
|
||||
addCriterion("original_state is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateEqualTo(String value) {
|
||||
addCriterion("original_state =", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateNotEqualTo(String value) {
|
||||
addCriterion("original_state <>", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateGreaterThan(String value) {
|
||||
addCriterion("original_state >", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateGreaterThanOrEqualTo(String value) {
|
||||
addCriterion("original_state >=", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateLessThan(String value) {
|
||||
addCriterion("original_state <", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateLessThanOrEqualTo(String value) {
|
||||
addCriterion("original_state <=", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateLike(String value) {
|
||||
addCriterion("original_state like", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateNotLike(String value) {
|
||||
addCriterion("original_state not like", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateIn(List<String> values) {
|
||||
addCriterion("original_state in", values, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateNotIn(List<String> values) {
|
||||
addCriterion("original_state not in", values, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateBetween(String value1, String value2) {
|
||||
addCriterion("original_state between", value1, value2, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateNotBetween(String value1, String value2) {
|
||||
addCriterion("original_state not between", value1, value2, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Criteria extends GeneratedCriteria {
|
||||
|
|
|
@ -43,5 +43,7 @@ public class ApiScenario implements Serializable {
|
|||
|
||||
private Integer num;
|
||||
|
||||
private String originalState;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
|
@ -1393,6 +1393,76 @@ public class ApiScenarioExample {
|
|||
addCriterion("num not between", value1, value2, "num");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateIsNull() {
|
||||
addCriterion("original_state is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateIsNotNull() {
|
||||
addCriterion("original_state is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateEqualTo(String value) {
|
||||
addCriterion("original_state =", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateNotEqualTo(String value) {
|
||||
addCriterion("original_state <>", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateGreaterThan(String value) {
|
||||
addCriterion("original_state >", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateGreaterThanOrEqualTo(String value) {
|
||||
addCriterion("original_state >=", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateLessThan(String value) {
|
||||
addCriterion("original_state <", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateLessThanOrEqualTo(String value) {
|
||||
addCriterion("original_state <=", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateLike(String value) {
|
||||
addCriterion("original_state like", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateNotLike(String value) {
|
||||
addCriterion("original_state not like", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateIn(List<String> values) {
|
||||
addCriterion("original_state in", values, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateNotIn(List<String> values) {
|
||||
addCriterion("original_state not in", values, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateBetween(String value1, String value2) {
|
||||
addCriterion("original_state between", value1, value2, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateNotBetween(String value1, String value2) {
|
||||
addCriterion("original_state not between", value1, value2, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Criteria extends GeneratedCriteria {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
<result column="path" jdbcType="VARCHAR" property="path" />
|
||||
<result column="num" jdbcType="INTEGER" property="num" />
|
||||
<result column="tags" jdbcType="VARCHAR" property="tags" />
|
||||
<result column="original_state" jdbcType="VARCHAR" property="originalState" />
|
||||
</resultMap>
|
||||
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.base.domain.ApiDefinitionWithBLOBs">
|
||||
<result column="description" jdbcType="LONGVARCHAR" property="description" />
|
||||
|
@ -84,7 +85,7 @@
|
|||
</sql>
|
||||
<sql id="Base_Column_List">
|
||||
id, project_id, `name`, `method`, module_path, environment_id, schedule, `status`,
|
||||
module_id, user_id, create_time, update_time, protocol, `path`, num, tags
|
||||
module_id, user_id, create_time, update_time, protocol, `path`, num, tags, original_state
|
||||
</sql>
|
||||
<sql id="Blob_Column_List">
|
||||
description, request, response
|
||||
|
@ -143,15 +144,15 @@
|
|||
schedule, `status`, module_id,
|
||||
user_id, create_time, update_time,
|
||||
protocol, `path`, num,
|
||||
tags, description, request,
|
||||
response)
|
||||
tags, original_state, description,
|
||||
request, response)
|
||||
values (#{id,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
|
||||
#{method,jdbcType=VARCHAR}, #{modulePath,jdbcType=VARCHAR}, #{environmentId,jdbcType=VARCHAR},
|
||||
#{schedule,jdbcType=VARCHAR}, #{status,jdbcType=VARCHAR}, #{moduleId,jdbcType=VARCHAR},
|
||||
#{userId,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT},
|
||||
#{protocol,jdbcType=VARCHAR}, #{path,jdbcType=VARCHAR}, #{num,jdbcType=INTEGER},
|
||||
#{tags,jdbcType=VARCHAR}, #{description,jdbcType=LONGVARCHAR}, #{request,jdbcType=LONGVARCHAR},
|
||||
#{response,jdbcType=LONGVARCHAR})
|
||||
#{tags,jdbcType=VARCHAR}, #{originalState,jdbcType=VARCHAR}, #{description,jdbcType=LONGVARCHAR},
|
||||
#{request,jdbcType=LONGVARCHAR}, #{response,jdbcType=LONGVARCHAR})
|
||||
</insert>
|
||||
<insert id="insertSelective" parameterType="io.metersphere.base.domain.ApiDefinitionWithBLOBs">
|
||||
insert into api_definition
|
||||
|
@ -204,6 +205,9 @@
|
|||
<if test="tags != null">
|
||||
tags,
|
||||
</if>
|
||||
<if test="originalState != null">
|
||||
original_state,
|
||||
</if>
|
||||
<if test="description != null">
|
||||
description,
|
||||
</if>
|
||||
|
@ -263,6 +267,9 @@
|
|||
<if test="tags != null">
|
||||
#{tags,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="originalState != null">
|
||||
#{originalState,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="description != null">
|
||||
#{description,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
|
@ -331,6 +338,9 @@
|
|||
<if test="record.tags != null">
|
||||
tags = #{record.tags,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.originalState != null">
|
||||
original_state = #{record.originalState,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.description != null">
|
||||
description = #{record.description,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
|
@ -363,6 +373,7 @@
|
|||
`path` = #{record.path,jdbcType=VARCHAR},
|
||||
num = #{record.num,jdbcType=INTEGER},
|
||||
tags = #{record.tags,jdbcType=VARCHAR},
|
||||
original_state = #{record.originalState,jdbcType=VARCHAR},
|
||||
description = #{record.description,jdbcType=LONGVARCHAR},
|
||||
request = #{record.request,jdbcType=LONGVARCHAR},
|
||||
response = #{record.response,jdbcType=LONGVARCHAR}
|
||||
|
@ -387,7 +398,8 @@
|
|||
protocol = #{record.protocol,jdbcType=VARCHAR},
|
||||
`path` = #{record.path,jdbcType=VARCHAR},
|
||||
num = #{record.num,jdbcType=INTEGER},
|
||||
tags = #{record.tags,jdbcType=VARCHAR}
|
||||
tags = #{record.tags,jdbcType=VARCHAR},
|
||||
original_state = #{record.originalState,jdbcType=VARCHAR}
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
|
@ -440,6 +452,9 @@
|
|||
<if test="tags != null">
|
||||
tags = #{tags,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="originalState != null">
|
||||
original_state = #{originalState,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="description != null">
|
||||
description = #{description,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
|
@ -469,6 +484,7 @@
|
|||
`path` = #{path,jdbcType=VARCHAR},
|
||||
num = #{num,jdbcType=INTEGER},
|
||||
tags = #{tags,jdbcType=VARCHAR},
|
||||
original_state = #{originalState,jdbcType=VARCHAR},
|
||||
description = #{description,jdbcType=LONGVARCHAR},
|
||||
request = #{request,jdbcType=LONGVARCHAR},
|
||||
response = #{response,jdbcType=LONGVARCHAR}
|
||||
|
@ -490,7 +506,8 @@
|
|||
protocol = #{protocol,jdbcType=VARCHAR},
|
||||
`path` = #{path,jdbcType=VARCHAR},
|
||||
num = #{num,jdbcType=INTEGER},
|
||||
tags = #{tags,jdbcType=VARCHAR}
|
||||
tags = #{tags,jdbcType=VARCHAR},
|
||||
original_state = #{originalState,jdbcType=VARCHAR}
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
</mapper>
|
|
@ -21,6 +21,7 @@
|
|||
<result column="last_result" jdbcType="VARCHAR" property="lastResult" />
|
||||
<result column="report_id" jdbcType="VARCHAR" property="reportId" />
|
||||
<result column="num" jdbcType="INTEGER" property="num" />
|
||||
<result column="original_state" jdbcType="VARCHAR" property="originalState" />
|
||||
</resultMap>
|
||||
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.base.domain.ApiScenarioWithBLOBs">
|
||||
<result column="scenario_definition" jdbcType="LONGVARCHAR" property="scenarioDefinition" />
|
||||
|
@ -87,7 +88,7 @@
|
|||
<sql id="Base_Column_List">
|
||||
id, project_id, tags, user_id, api_scenario_module_id, module_path, `name`, `level`,
|
||||
`status`, principal, step_total, follow_people, schedule, create_time, update_time,
|
||||
pass_rate, last_result, report_id, num
|
||||
pass_rate, last_result, report_id, num, original_state
|
||||
</sql>
|
||||
<sql id="Blob_Column_List">
|
||||
scenario_definition, description
|
||||
|
@ -147,16 +148,16 @@
|
|||
principal, step_total, follow_people,
|
||||
schedule, create_time, update_time,
|
||||
pass_rate, last_result, report_id,
|
||||
num, scenario_definition, description
|
||||
)
|
||||
num, original_state, scenario_definition,
|
||||
description)
|
||||
values (#{id,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{tags,jdbcType=VARCHAR},
|
||||
#{userId,jdbcType=VARCHAR}, #{apiScenarioModuleId,jdbcType=VARCHAR}, #{modulePath,jdbcType=VARCHAR},
|
||||
#{name,jdbcType=VARCHAR}, #{level,jdbcType=VARCHAR}, #{status,jdbcType=VARCHAR},
|
||||
#{principal,jdbcType=VARCHAR}, #{stepTotal,jdbcType=INTEGER}, #{followPeople,jdbcType=VARCHAR},
|
||||
#{schedule,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT},
|
||||
#{passRate,jdbcType=VARCHAR}, #{lastResult,jdbcType=VARCHAR}, #{reportId,jdbcType=VARCHAR},
|
||||
#{num,jdbcType=INTEGER}, #{scenarioDefinition,jdbcType=LONGVARCHAR}, #{description,jdbcType=LONGVARCHAR}
|
||||
)
|
||||
#{num,jdbcType=INTEGER}, #{originalState,jdbcType=VARCHAR}, #{scenarioDefinition,jdbcType=LONGVARCHAR},
|
||||
#{description,jdbcType=LONGVARCHAR})
|
||||
</insert>
|
||||
<insert id="insertSelective" parameterType="io.metersphere.base.domain.ApiScenarioWithBLOBs">
|
||||
insert into api_scenario
|
||||
|
@ -218,6 +219,9 @@
|
|||
<if test="num != null">
|
||||
num,
|
||||
</if>
|
||||
<if test="originalState != null">
|
||||
original_state,
|
||||
</if>
|
||||
<if test="scenarioDefinition != null">
|
||||
scenario_definition,
|
||||
</if>
|
||||
|
@ -283,6 +287,9 @@
|
|||
<if test="num != null">
|
||||
#{num,jdbcType=INTEGER},
|
||||
</if>
|
||||
<if test="originalState != null">
|
||||
#{originalState,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="scenarioDefinition != null">
|
||||
#{scenarioDefinition,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
|
@ -357,6 +364,9 @@
|
|||
<if test="record.num != null">
|
||||
num = #{record.num,jdbcType=INTEGER},
|
||||
</if>
|
||||
<if test="record.originalState != null">
|
||||
original_state = #{record.originalState,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.scenarioDefinition != null">
|
||||
scenario_definition = #{record.scenarioDefinition,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
|
@ -389,6 +399,7 @@
|
|||
last_result = #{record.lastResult,jdbcType=VARCHAR},
|
||||
report_id = #{record.reportId,jdbcType=VARCHAR},
|
||||
num = #{record.num,jdbcType=INTEGER},
|
||||
original_state = #{record.originalState,jdbcType=VARCHAR},
|
||||
scenario_definition = #{record.scenarioDefinition,jdbcType=LONGVARCHAR},
|
||||
description = #{record.description,jdbcType=LONGVARCHAR}
|
||||
<if test="_parameter != null">
|
||||
|
@ -415,7 +426,8 @@
|
|||
pass_rate = #{record.passRate,jdbcType=VARCHAR},
|
||||
last_result = #{record.lastResult,jdbcType=VARCHAR},
|
||||
report_id = #{record.reportId,jdbcType=VARCHAR},
|
||||
num = #{record.num,jdbcType=INTEGER}
|
||||
num = #{record.num,jdbcType=INTEGER},
|
||||
original_state = #{record.originalState,jdbcType=VARCHAR}
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
|
@ -477,6 +489,9 @@
|
|||
<if test="num != null">
|
||||
num = #{num,jdbcType=INTEGER},
|
||||
</if>
|
||||
<if test="originalState != null">
|
||||
original_state = #{originalState,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="scenarioDefinition != null">
|
||||
scenario_definition = #{scenarioDefinition,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
|
@ -506,6 +521,7 @@
|
|||
last_result = #{lastResult,jdbcType=VARCHAR},
|
||||
report_id = #{reportId,jdbcType=VARCHAR},
|
||||
num = #{num,jdbcType=INTEGER},
|
||||
original_state = #{originalState,jdbcType=VARCHAR},
|
||||
scenario_definition = #{scenarioDefinition,jdbcType=LONGVARCHAR},
|
||||
description = #{description,jdbcType=LONGVARCHAR}
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
|
@ -529,7 +545,8 @@
|
|||
pass_rate = #{passRate,jdbcType=VARCHAR},
|
||||
last_result = #{lastResult,jdbcType=VARCHAR},
|
||||
report_id = #{reportId,jdbcType=VARCHAR},
|
||||
num = #{num,jdbcType=INTEGER}
|
||||
num = #{num,jdbcType=INTEGER},
|
||||
original_state = #{originalState,jdbcType=VARCHAR}
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
</mapper>
|
|
@ -235,7 +235,7 @@
|
|||
|
||||
<update id="removeToGc">
|
||||
update api_definition
|
||||
set
|
||||
set original_state=status,
|
||||
status = 'Trash'
|
||||
where id in
|
||||
<foreach collection="ids" item="v" separator="," open="(" close=")">
|
||||
|
@ -245,7 +245,7 @@
|
|||
|
||||
<update id="removeToGcByExample" parameterType="io.metersphere.base.domain.ApiDefinitionExample">
|
||||
update api_definition
|
||||
set
|
||||
set original_state=status,
|
||||
status = 'Trash', module_path = null, module_id = null
|
||||
<if test="_parameter != null">
|
||||
<include refid="Example_Where_Clause" />
|
||||
|
@ -255,7 +255,7 @@
|
|||
<update id="reduction">
|
||||
update api_definition
|
||||
set
|
||||
status = 'Underway'
|
||||
status = original_state
|
||||
where id in
|
||||
<foreach collection="ids" item="v" separator="," open="(" close=")">
|
||||
#{v}
|
||||
|
|
|
@ -275,7 +275,7 @@
|
|||
|
||||
<update id="removeToGc">
|
||||
update api_scenario
|
||||
set
|
||||
set original_state=status,
|
||||
status = 'Trash'
|
||||
where id in
|
||||
<foreach collection="ids" item="v" separator="," open="(" close=")">
|
||||
|
@ -285,7 +285,7 @@
|
|||
|
||||
<update id="removeToGcByExample" parameterType="io.metersphere.base.domain.ApiScenarioExample">
|
||||
update api_scenario
|
||||
set
|
||||
set original_state=status,
|
||||
status = 'Trash', module_path = null, api_scenario_module_id = null
|
||||
<if test="_parameter != null">
|
||||
<include refid="Example_Where_Clause" />
|
||||
|
@ -295,7 +295,7 @@
|
|||
<update id="reduction">
|
||||
update api_scenario
|
||||
set
|
||||
status = 'Underway'
|
||||
status = original_state
|
||||
where id in
|
||||
<foreach collection="ids" item="v" separator="," open="(" close=")">
|
||||
#{v}
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
|
||||
<insert id="insertIfNotExists" parameterType="io.metersphere.base.domain.TestPlanApiScenario">
|
||||
-- 查询没有数据再插入
|
||||
INSERT INTO test_plan_api_scenario(id, test_plan_id, api_scenario_id, create_time, update_time)
|
||||
SELECT #{request.id}, #{request.testPlanId}, #{request.apiScenarioId}, #{request.createTime}, #{request.updateTime}
|
||||
INSERT INTO test_plan_api_scenario(id, test_plan_id, api_scenario_id, create_time, update_time, environment)
|
||||
SELECT #{request.id}, #{request.testPlanId}, #{request.apiScenarioId}, #{request.createTime}, #{request.updateTime}, #{request.environment}
|
||||
FROM DUAL
|
||||
WHERE NOT EXISTS(
|
||||
SELECT id FROM
|
||||
|
|
|
@ -1,18 +1,35 @@
|
|||
package io.metersphere.commons.user;
|
||||
|
||||
import io.metersphere.commons.utils.CodingUtil;
|
||||
import io.metersphere.dto.UserDTO;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public class SessionUser extends UserDTO implements Serializable {
|
||||
public static final String secret = "9a9rdqPlTqhpZzkq";
|
||||
public static final String iv = "1Av7hf9PgHusUHRm";
|
||||
|
||||
private static final long serialVersionUID = -7149638440406959033L;
|
||||
private String csrfToken;
|
||||
|
||||
private SessionUser() {
|
||||
}
|
||||
|
||||
public static SessionUser fromUser(UserDTO user) {
|
||||
SessionUser sessionUser = new SessionUser();
|
||||
BeanUtils.copyProperties(user, sessionUser);
|
||||
|
||||
List<String> infos = Arrays.asList(user.getId(), RandomStringUtils.random(6), "" + System.currentTimeMillis());
|
||||
sessionUser.csrfToken = CodingUtil.aesEncrypt(StringUtils.join(infos, "|"), secret, iv);
|
||||
return sessionUser;
|
||||
}
|
||||
|
||||
|
|
|
@ -62,14 +62,14 @@ public class SessionUtils {
|
|||
}
|
||||
|
||||
public static String getCurrentWorkspaceId() {
|
||||
return Optional.ofNullable(getUser()).orElse(new SessionUser()).getLastWorkspaceId();
|
||||
return getUser().getLastWorkspaceId();
|
||||
}
|
||||
|
||||
public static String getCurrentOrganizationId() {
|
||||
return Optional.ofNullable(getUser()).orElse(new SessionUser()).getLastOrganizationId();
|
||||
return getUser().getLastOrganizationId();
|
||||
}
|
||||
|
||||
public static String getCurrentProjectId() {
|
||||
return Optional.ofNullable(getUser()).orElse(new SessionUser()).getLastProjectId();
|
||||
return getUser().getLastProjectId();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,10 @@ public class ShiroUtils {
|
|||
// filterChainDefinitionMap.put("/document/**", "anon");
|
||||
}
|
||||
|
||||
public static void ignoreCsrfFilter(Map<String, String> filterChainDefinitionMap) {
|
||||
filterChainDefinitionMap.put("/", "apikey, authc"); // 跳转到 / 不用校验 csrf
|
||||
}
|
||||
|
||||
public static Cookie getSessionIdCookie(){
|
||||
SimpleCookie sessionIdCookie = new SimpleCookie();
|
||||
sessionIdCookie.setPath("/");
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.metersphere.config;
|
|||
|
||||
import io.metersphere.commons.utils.ShiroUtils;
|
||||
import io.metersphere.security.ApiKeyFilter;
|
||||
import io.metersphere.security.CsrfFilter;
|
||||
import io.metersphere.security.UserModularRealmAuthenticator;
|
||||
import io.metersphere.security.realm.LdapRealm;
|
||||
import io.metersphere.security.realm.ShiroDBRealm;
|
||||
|
@ -44,10 +45,14 @@ public class ShiroConfig implements EnvironmentAware {
|
|||
shiroFilterFactoryBean.setSuccessUrl("/");
|
||||
|
||||
shiroFilterFactoryBean.getFilters().put("apikey", new ApiKeyFilter());
|
||||
shiroFilterFactoryBean.getFilters().put("csrf", new CsrfFilter());
|
||||
Map<String, String> filterChainDefinitionMap = shiroFilterFactoryBean.getFilterChainDefinitionMap();
|
||||
|
||||
ShiroUtils.loadBaseFilterChain(filterChainDefinitionMap);
|
||||
|
||||
filterChainDefinitionMap.put("/**", "apikey, authc");
|
||||
ShiroUtils.ignoreCsrfFilter(filterChainDefinitionMap);
|
||||
|
||||
filterChainDefinitionMap.put("/**", "apikey, csrf, authc");
|
||||
return shiroFilterFactoryBean;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package io.metersphere.controller;
|
||||
|
||||
import io.metersphere.commons.constants.SsoMode;
|
||||
import io.metersphere.commons.constants.UserSource;
|
||||
import io.metersphere.commons.user.SessionUser;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
|
@ -10,7 +9,6 @@ import io.metersphere.service.UserService;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
@ -24,8 +22,6 @@ public class LoginController {
|
|||
@Resource
|
||||
private UserService userService;
|
||||
@Resource
|
||||
private Environment env;
|
||||
@Resource
|
||||
private BaseDisplayService baseDisplayService;
|
||||
|
||||
@GetMapping(value = "/isLogin")
|
||||
|
@ -37,10 +33,6 @@ public class LoginController {
|
|||
}
|
||||
return ResultHolder.success(user);
|
||||
}
|
||||
String ssoMode = env.getProperty("sso.mode");
|
||||
if (ssoMode != null && StringUtils.equalsIgnoreCase(SsoMode.CAS.name(), ssoMode)) {
|
||||
return ResultHolder.error("sso");
|
||||
}
|
||||
return ResultHolder.error("");
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
package io.metersphere.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.github.pagehelper.Page;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import io.metersphere.base.domain.Organization;
|
||||
import io.metersphere.base.domain.User;
|
||||
import io.metersphere.base.domain.Workspace;
|
||||
import io.metersphere.commons.constants.RoleConstants;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.user.SessionUser;
|
||||
|
@ -29,7 +26,6 @@ import io.metersphere.service.WorkspaceService;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.authz.annotation.Logical;
|
||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||
import org.checkerframework.checker.units.qual.C;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
|
|
|
@ -14,8 +14,7 @@ public class EngineContext {
|
|||
private String reportId;
|
||||
private Integer resourceIndex;
|
||||
private Map<String, Object> properties = new HashMap<>();
|
||||
private Map<String, String> testData = new HashMap<>();
|
||||
private Map<String, byte[]> testJars = new HashMap<>();
|
||||
private Map<String, byte[]> testResourceFiles = new HashMap<>();
|
||||
|
||||
public String getTestId() {
|
||||
return testId;
|
||||
|
@ -69,14 +68,6 @@ public class EngineContext {
|
|||
this.fileType = fileType;
|
||||
}
|
||||
|
||||
public Map<String, String> getTestData() {
|
||||
return testData;
|
||||
}
|
||||
|
||||
public void setTestData(Map<String, String> testData) {
|
||||
this.testData = testData;
|
||||
}
|
||||
|
||||
public String getResourcePoolId() {
|
||||
return resourcePoolId;
|
||||
}
|
||||
|
@ -111,11 +102,11 @@ public class EngineContext {
|
|||
}
|
||||
|
||||
|
||||
public Map<String, byte[]> getTestJars() {
|
||||
return testJars;
|
||||
public Map<String, byte[]> getTestResourceFiles() {
|
||||
return testResourceFiles;
|
||||
}
|
||||
|
||||
public void setTestJars(Map<String, byte[]> testJars) {
|
||||
this.testJars = testJars;
|
||||
public void setTestResourceFiles(Map<String, byte[]> testResourceFiles) {
|
||||
this.testResourceFiles = testResourceFiles;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,8 @@ import io.metersphere.service.FileService;
|
|||
import io.metersphere.service.KubernetesTestEngine;
|
||||
import io.metersphere.service.TestResourcePoolService;
|
||||
import org.apache.commons.beanutils.ConstructorUtils;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.collections4.ListUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.reflections8.Reflections;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -92,8 +93,7 @@ public class EngineFactory {
|
|||
}
|
||||
|
||||
List<FileMetadata> jmxFiles = fileMetadataList.stream().filter(f -> StringUtils.equalsIgnoreCase(f.getType(), FileType.JMX.name())).collect(Collectors.toList());
|
||||
List<FileMetadata> csvFiles = fileMetadataList.stream().filter(f -> StringUtils.equalsIgnoreCase(f.getType(), FileType.CSV.name())).collect(Collectors.toList());
|
||||
List<FileMetadata> jarFiles = fileMetadataList.stream().filter(f -> StringUtils.equalsIgnoreCase(f.getType(), FileType.JAR.name())).collect(Collectors.toList());
|
||||
List<FileMetadata> resourceFiles = ListUtils.subtract(fileMetadataList, jmxFiles);
|
||||
// 合并上传的jmx
|
||||
byte[] jmxBytes = mergeJmx(jmxFiles);
|
||||
final EngineContext engineContext = new EngineContext();
|
||||
|
@ -156,22 +156,13 @@ public class EngineFactory {
|
|||
MSException.throwException(e);
|
||||
}
|
||||
|
||||
if (CollectionUtils.isNotEmpty(csvFiles)) {
|
||||
Map<String, String> data = new HashMap<>();
|
||||
csvFiles.forEach(cf -> {
|
||||
FileContent csvContent = fileService.getFileContent(cf.getId());
|
||||
data.put(cf.getName(), new String(csvContent.getFile()));
|
||||
});
|
||||
engineContext.setTestData(data);
|
||||
}
|
||||
|
||||
if (CollectionUtils.isNotEmpty(jarFiles)) {
|
||||
if (CollectionUtils.isNotEmpty(resourceFiles)) {
|
||||
Map<String, byte[]> data = new HashMap<>();
|
||||
jarFiles.forEach(jf -> {
|
||||
FileContent content = fileService.getFileContent(jf.getId());
|
||||
data.put(jf.getName(), content.getFile());
|
||||
resourceFiles.forEach(cf -> {
|
||||
FileContent csvContent = fileService.getFileContent(cf.getId());
|
||||
data.put(cf.getName(), csvContent.getFile());
|
||||
});
|
||||
engineContext.setTestJars(data);
|
||||
engineContext.setTestResourceFiles(data);
|
||||
}
|
||||
|
||||
return engineContext;
|
||||
|
|
|
@ -51,17 +51,9 @@ public class JmeterFileService {
|
|||
|
||||
// 每个测试生成一个文件夹
|
||||
files.put(fileName, context.getContent().getBytes(StandardCharsets.UTF_8));
|
||||
// 保存测试数据文件
|
||||
Map<String, String> testData = context.getTestData();
|
||||
if (!CollectionUtils.isEmpty(testData)) {
|
||||
for (String k : testData.keySet()) {
|
||||
String v = testData.get(k);
|
||||
files.put(k, v.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
// 保存 byte[] jar
|
||||
Map<String, byte[]> jarFiles = context.getTestJars();
|
||||
// 保存 byte[]
|
||||
Map<String, byte[]> jarFiles = context.getTestResourceFiles();
|
||||
if (!CollectionUtils.isEmpty(jarFiles)) {
|
||||
for (String k : jarFiles.keySet()) {
|
||||
byte[] v = jarFiles.get(k);
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
package io.metersphere.security;
|
||||
|
||||
import io.metersphere.commons.user.SessionUser;
|
||||
import io.metersphere.commons.utils.CodingUtil;
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.web.filter.authc.AnonymousFilter;
|
||||
import org.apache.shiro.web.util.WebUtils;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public class CsrfFilter extends AnonymousFilter {
|
||||
private static final String TOKEN_NAME = "CSRF-TOKEN";
|
||||
|
||||
@Override
|
||||
protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) {
|
||||
HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
|
||||
|
||||
if (!SecurityUtils.getSubject().isAuthenticated()) {
|
||||
((HttpServletResponse) response).setHeader("Authentication-Status", "invalid");
|
||||
return true;
|
||||
}
|
||||
// api 过来的请求
|
||||
if (ApiKeyHandler.isApiKeyCall(WebUtils.toHttp(request))) {
|
||||
return true;
|
||||
}
|
||||
// websocket 不需要csrf
|
||||
String websocketKey = httpServletRequest.getHeader("Sec-WebSocket-Key");
|
||||
if (StringUtils.isNotBlank(websocketKey)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 请求头取出的token value
|
||||
String csrfToken = httpServletRequest.getHeader(TOKEN_NAME);
|
||||
// 校验 token
|
||||
validateToken(csrfToken);
|
||||
// 校验 referer
|
||||
validateReferer(httpServletRequest);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void validateReferer(HttpServletRequest request) {
|
||||
Environment env = CommonBeanFactory.getBean(Environment.class);
|
||||
String domains = env.getProperty("referer.urls");
|
||||
if (StringUtils.isBlank(domains)) {
|
||||
// 没有配置不校验
|
||||
return;
|
||||
}
|
||||
|
||||
String[] split = StringUtils.split(domains, ",");
|
||||
String referer = request.getHeader(HttpHeaders.REFERER);
|
||||
if (split != null) {
|
||||
if (!ArrayUtils.contains(split, referer)) {
|
||||
throw new RuntimeException("csrf error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateToken(String csrfToken) {
|
||||
if (StringUtils.isBlank(csrfToken)) {
|
||||
throw new RuntimeException("csrf token is empty");
|
||||
}
|
||||
csrfToken = CodingUtil.aesDecrypt(csrfToken, SessionUser.secret, SessionUser.iv);
|
||||
|
||||
String[] signatureArray = StringUtils.split(StringUtils.trimToNull(csrfToken), "|");
|
||||
if (signatureArray.length != 3) {
|
||||
throw new RuntimeException("invalid token");
|
||||
}
|
||||
|
||||
long signatureTime;
|
||||
try {
|
||||
signatureTime = Long.parseLong(signatureArray[2]);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
Environment env = CommonBeanFactory.getBean(Environment.class);
|
||||
long timeout = env.getProperty("session.timeout", Long.class, 43200L);
|
||||
if (Math.abs(System.currentTimeMillis() - signatureTime) > timeout * 1000) {
|
||||
throw new RuntimeException("expired token");
|
||||
}
|
||||
if (!StringUtils.equals(SessionUtils.getUserId(), signatureArray[0])) {
|
||||
throw new RuntimeException("Please check csrf token.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -136,8 +136,8 @@ public class XmindCaseParser {
|
|||
data.setNodePath(nodePath);
|
||||
|
||||
|
||||
if (data.getName().length() > 50) {
|
||||
process.add(Translator.get("test_case") + Translator.get("test_track.length_less_than") + "50", nodePath + data.getName());
|
||||
if (data.getName().length() > 200) {
|
||||
process.add(Translator.get("test_case") + Translator.get("test_track.length_less_than") + "200", nodePath + data.getName());
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(nodePath)) {
|
||||
|
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* 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.jmeter.assertions;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.samplers.SampleResult;
|
||||
import org.apache.jmeter.testelement.AbstractTestElement;
|
||||
import org.apache.jmeter.testelement.ThreadListener;
|
||||
import org.apache.jmeter.util.JMeterUtils;
|
||||
import org.apache.oro.text.regex.Pattern;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This is main class for JSONPath Assertion which verifies assertion on
|
||||
* previous sample result using JSON path expression
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public class JSONPathAssertion extends AbstractTestElement implements Serializable, Assertion, ThreadListener {
|
||||
private static final Logger log = LoggerFactory.getLogger(JSONPathAssertion.class);
|
||||
private static final long serialVersionUID = 2L;
|
||||
public static final String JSONPATH = "JSON_PATH";
|
||||
public static final String EXPECTEDVALUE = "EXPECTED_VALUE";
|
||||
public static final String JSONVALIDATION = "JSONVALIDATION";
|
||||
public static final String EXPECT_NULL = "EXPECT_NULL";
|
||||
public static final String INVERT = "INVERT";
|
||||
public static final String ISREGEX = "ISREGEX";
|
||||
|
||||
private static ThreadLocal<DecimalFormat> decimalFormatter =
|
||||
ThreadLocal.withInitial(JSONPathAssertion::createDecimalFormat);
|
||||
|
||||
public String getOption() {
|
||||
return getPropertyAsString("ASS_OPTION");
|
||||
}
|
||||
|
||||
private static DecimalFormat createDecimalFormat() {
|
||||
DecimalFormat decimalFormatter = new DecimalFormat("#.#");
|
||||
decimalFormatter.setMaximumFractionDigits(340); // java.text.DecimalFormat.DOUBLE_FRACTION_DIGITS == 340
|
||||
decimalFormatter.setMinimumFractionDigits(1);
|
||||
return decimalFormatter;
|
||||
}
|
||||
|
||||
public String getJsonPath() {
|
||||
return getPropertyAsString(JSONPATH);
|
||||
}
|
||||
|
||||
public void setJsonPath(String jsonPath) {
|
||||
setProperty(JSONPATH, jsonPath);
|
||||
}
|
||||
|
||||
public String getExpectedValue() {
|
||||
return getPropertyAsString(EXPECTEDVALUE);
|
||||
}
|
||||
|
||||
public void setExpectedValue(String expectedValue) {
|
||||
setProperty(EXPECTEDVALUE, expectedValue);
|
||||
}
|
||||
|
||||
public void setJsonValidationBool(boolean jsonValidation) {
|
||||
setProperty(JSONVALIDATION, jsonValidation);
|
||||
}
|
||||
|
||||
public void setExpectNull(boolean val) {
|
||||
setProperty(EXPECT_NULL, val);
|
||||
}
|
||||
|
||||
public boolean isExpectNull() {
|
||||
return getPropertyAsBoolean(EXPECT_NULL);
|
||||
}
|
||||
|
||||
public boolean isJsonValidationBool() {
|
||||
return getPropertyAsBoolean(JSONVALIDATION);
|
||||
}
|
||||
|
||||
public void setInvert(boolean invert) {
|
||||
setProperty(INVERT, invert);
|
||||
}
|
||||
|
||||
public boolean isInvert() {
|
||||
return getPropertyAsBoolean(INVERT);
|
||||
}
|
||||
|
||||
public void setIsRegex(boolean flag) {
|
||||
setProperty(ISREGEX, flag);
|
||||
}
|
||||
|
||||
public boolean isUseRegex() {
|
||||
return getPropertyAsBoolean(ISREGEX, true);
|
||||
}
|
||||
|
||||
private void doAssert(String jsonString) {
|
||||
Object value = JsonPath.read(jsonString, getJsonPath());
|
||||
|
||||
if (!isJsonValidationBool()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value instanceof JSONArray) {
|
||||
if (arrayMatched((JSONArray) value)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if ((isExpectNull() && value == null)
|
||||
|| isEquals(value)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isExpectNull()) {
|
||||
throw new IllegalStateException(String.format("Value expected to be null, but found '%s'", value));
|
||||
} else {
|
||||
String msg;
|
||||
if (isUseRegex()) {
|
||||
msg = "Value expected to match regexp '%s', but it did not match: '%s'";
|
||||
} else {
|
||||
msg = "Value expected to be '%s', but found '%s'";
|
||||
}
|
||||
throw new IllegalStateException(String.format(msg, getExpectedValue(), objectToString(value)));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean arrayMatched(JSONArray value) {
|
||||
if (value.isEmpty() && "[]".equals(getExpectedValue())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (Object subj : value.toArray()) {
|
||||
if ((subj == null && isExpectNull())
|
||||
|| isEquals(subj)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return isEquals(value);
|
||||
}
|
||||
|
||||
private boolean isEquals(Object subj) {
|
||||
String str = objectToString(subj);
|
||||
if (isUseRegex()) {
|
||||
Pattern pattern = JMeterUtils.getPatternCache().getPattern(getExpectedValue());
|
||||
return JMeterUtils.getMatcher().matches(str, pattern);
|
||||
} else {
|
||||
if (StringUtils.isNotEmpty(getOption())) {
|
||||
boolean refFlag = false;
|
||||
switch (getOption()) {
|
||||
case "CONTAINS":
|
||||
refFlag = str.contains(getExpectedValue());
|
||||
break;
|
||||
case "NOT_CONTAINS":
|
||||
refFlag = !str.contains(getExpectedValue());
|
||||
break;
|
||||
case "EQUALS":
|
||||
refFlag = str.equals(getExpectedValue());
|
||||
break;
|
||||
case "NOT_EQUALS":
|
||||
refFlag = !str.contains(getExpectedValue());
|
||||
break;
|
||||
}
|
||||
return refFlag;
|
||||
}
|
||||
return str.equals(getExpectedValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssertionResult getResult(SampleResult samplerResult) {
|
||||
AssertionResult result = new AssertionResult(getName());
|
||||
String responseData = samplerResult.getResponseDataAsString();
|
||||
if (responseData.isEmpty()) {
|
||||
return result.setResultForNull();
|
||||
}
|
||||
|
||||
result.setFailure(false);
|
||||
result.setFailureMessage("");
|
||||
|
||||
if (!isInvert()) {
|
||||
try {
|
||||
doAssert(responseData);
|
||||
} catch (Exception e) {
|
||||
log.debug("Assertion failed", e);
|
||||
result.setFailure(true);
|
||||
result.setFailureMessage(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
doAssert(responseData);
|
||||
result.setFailure(true);
|
||||
if (isJsonValidationBool()) {
|
||||
if (isExpectNull()) {
|
||||
result.setFailureMessage("Failed that JSONPath " + getJsonPath() + " not matches null");
|
||||
} else {
|
||||
result.setFailureMessage("Failed that JSONPath " + getJsonPath() + " not matches " + getExpectedValue());
|
||||
}
|
||||
} else {
|
||||
result.setFailureMessage("Failed that JSONPath not exists: " + getJsonPath());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("Assertion failed, as expected", e);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String objectToString(Object subj) {
|
||||
String str;
|
||||
if (subj == null) {
|
||||
str = "null";
|
||||
} else if (subj instanceof Map) {
|
||||
//noinspection unchecked
|
||||
str = new JSONObject((Map<String, Object>) subj).toJSONString();
|
||||
} else if (subj instanceof Double || subj instanceof Float) {
|
||||
str = decimalFormatter.get().format(subj);
|
||||
} else {
|
||||
str = subj.toString();
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void threadStarted() {
|
||||
// nothing to do on thread start
|
||||
}
|
||||
|
||||
@Override
|
||||
public void threadFinished() {
|
||||
decimalFormatter.remove();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -45,3 +45,9 @@ alter table test_plan_api_scenario change environment_id environment longtext nu
|
|||
|
||||
-- file add sort column
|
||||
alter table file_metadata add sort int default 0;
|
||||
|
||||
-- add Original state
|
||||
alter table api_definition add original_state varchar(64);
|
||||
alter table api_scenario add original_state varchar(64);
|
||||
update api_definition set original_state='Underway';
|
||||
update api_scenario set original_state='Underway';
|
|
@ -17,7 +17,6 @@
|
|||
"@fortawesome/vue-fontawesome": "^0.1.9",
|
||||
"axios": "^0.21.1",
|
||||
"core-js": "^3.4.3",
|
||||
"default-passive-events": "^2.0.0",
|
||||
"diffable-html": "^4.0.0",
|
||||
"echarts": "^4.6.0",
|
||||
"el-table-infinite-scroll": "^1.0.10",
|
||||
|
|
|
@ -30,6 +30,7 @@ import MsUser from "./components/common/head/HeaderUser";
|
|||
import MsHeaderOrgWs from "./components/common/head/HeaderOrgWs";
|
||||
import MsLanguageSwitch from "./components/common/head/LanguageSwitch";
|
||||
import {saveLocalStorage} from "@/common/js/utils";
|
||||
import {registerRequestHeaders} from "@/common/js/ajax";
|
||||
|
||||
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
|
||||
const header = requireComponent.keys().length > 0 ? requireComponent("./license/LicenseMessage.vue") : {};
|
||||
|
@ -53,6 +54,7 @@ export default {
|
|||
window.addEventListener("beforeunload", () => {
|
||||
localStorage.setItem("store", JSON.stringify(this.$store.state))
|
||||
})
|
||||
registerRequestHeaders();
|
||||
},
|
||||
beforeCreate() {
|
||||
this.$get("/isLogin").then(response => {
|
||||
|
|
|
@ -100,6 +100,19 @@
|
|||
active() {
|
||||
this.isActive = !this.isActive;
|
||||
},
|
||||
formatResult(res) {
|
||||
let resMap = new Map;
|
||||
if (res && res.scenarios) {
|
||||
res.scenarios.forEach(item => {
|
||||
if (item && item.requestResults) {
|
||||
item.requestResults.forEach(req => {
|
||||
resMap.set(req.id, req);
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
this.$emit('refresh', resMap);
|
||||
},
|
||||
getReport() {
|
||||
this.init();
|
||||
if (this.reportId) {
|
||||
|
@ -113,7 +126,7 @@
|
|||
if (!this.content) {
|
||||
this.content = {scenarios: []};
|
||||
}
|
||||
this.$emit('refresh');
|
||||
this.formatResult(this.content);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@
|
|||
<!--测试计划-->
|
||||
<el-drawer :visible.sync="planVisible" :destroy-on-close="true" direction="ltr" :withHeader="false"
|
||||
:title="$t('test_track.plan_view.test_result')" :modal="false" size="90%">
|
||||
<ms-test-plan-list @addTestPlan="addTestPlan" @cancel="cancel"/>
|
||||
<ms-test-plan-list @addTestPlan="addTestPlan(arguments)" @cancel="cancel" ref="testPlanList" :row="selectRows"/>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</el-card>
|
||||
|
@ -276,7 +276,9 @@
|
|||
},
|
||||
{
|
||||
name: this.$t('test_track.case.batch_move_case'), handleClick: this.handleBatchMove
|
||||
}
|
||||
},
|
||||
{name: this.$t('api_test.definition.request.batch_delete'), handleClick: this.handleDeleteBatch},
|
||||
|
||||
],
|
||||
isSelectAllDate: false,
|
||||
selectRows: new Set(),
|
||||
|
@ -318,7 +320,7 @@
|
|||
],
|
||||
principal: [],
|
||||
environmentId: [],
|
||||
projectEnv: []
|
||||
projectEnv: [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -496,17 +498,31 @@
|
|||
cancel() {
|
||||
this.planVisible = false;
|
||||
},
|
||||
addTestPlan(plans) {
|
||||
let obj = {planIds: plans, scenarioIds: this.selection};
|
||||
addTestPlan(params) {
|
||||
let obj = {planIds: params[0], scenarioIds: this.selection};
|
||||
|
||||
obj.projectId = getCurrentProjectID();
|
||||
obj.selectAllDate = this.isSelectAllDate;
|
||||
obj.unSelectIds = this.unSelection;
|
||||
obj = Object.assign(obj, this.condition);
|
||||
// obj.projectId = getCurrentProjectID();
|
||||
// obj.selectAllDate = this.isSelectAllDate;
|
||||
// obj.unSelectIds = this.unSelection;
|
||||
// obj = Object.assign(obj, this.condition);
|
||||
|
||||
// todo 选取全部数据
|
||||
if (this.isSelectAllDate) {
|
||||
this.$warning("暂不支持批量添加所有场景到测试计划!");
|
||||
}
|
||||
|
||||
this.planVisible = false;
|
||||
|
||||
let map = new Map();
|
||||
this.selectRows.forEach(row => {
|
||||
map.set(row.id, row.projectIds);
|
||||
})
|
||||
obj.mapping = strMapToObj(map);
|
||||
obj.envMap = strMapToObj(params[1]);
|
||||
|
||||
this.$post("/api/automation/scenario/plan", obj, response => {
|
||||
this.$success(this.$t("commons.save_success"));
|
||||
this.search();
|
||||
});
|
||||
},
|
||||
getReport() {
|
||||
|
@ -582,6 +598,29 @@
|
|||
this.search();
|
||||
})
|
||||
},
|
||||
handleDeleteBatch(row) {
|
||||
if (this.trashEnable) {
|
||||
let ids = Array.from(this.selectRows).map(row => row.id);
|
||||
this.$post('/api/automation/deleteBatch/', ids, () => {
|
||||
this.$success(this.$t('commons.delete_success'));
|
||||
this.search();
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.$alert(this.$t('api_test.definition.request.delete_confirm') + " ?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
let ids = Array.from(this.selectRows).map(row => row.id);
|
||||
this.$post('/api/automation/removeToGc/', ids, () => {
|
||||
this.$success(this.$t('commons.delete_success'));
|
||||
this.search();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
execute(row) {
|
||||
this.infoDb = false;
|
||||
let url = "/api/automation/run";
|
||||
|
|
|
@ -184,7 +184,7 @@
|
|||
<!-- 调试结果 -->
|
||||
<el-drawer v-if="type!=='detail'" :visible.sync="debugVisible" :destroy-on-close="true" direction="ltr"
|
||||
:withHeader="true" :modal="false" size="90%">
|
||||
<ms-api-report-detail :report-id="reportId" :debug="true" :currentProjectId="projectId"/>
|
||||
<ms-api-report-detail :report-id="reportId" :debug="true" :currentProjectId="projectId" @refresh="detailRefresh"/>
|
||||
</el-drawer>
|
||||
|
||||
<!--场景公共参数-->
|
||||
|
@ -224,6 +224,7 @@
|
|||
import MsComponentConfig from "./component/ComponentConfig";
|
||||
import {handleCtrlSEvent} from "../../../../../common/js/utils";
|
||||
import EnvPopover from "@/business/components/api/automation/scenario/EnvPopover";
|
||||
|
||||
let jsonPath = require('jsonpath');
|
||||
export default {
|
||||
name: "EditApiScenario",
|
||||
|
@ -290,7 +291,8 @@
|
|||
response: {},
|
||||
projectIds: new Set,
|
||||
projectEnvMap: new Map,
|
||||
projectList: []
|
||||
projectList: [],
|
||||
debugResult: new Map,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
@ -400,7 +402,7 @@
|
|||
},
|
||||
{
|
||||
title: this.$t('api_test.automation.scenario_import'),
|
||||
show:this.showButton("scenario"),
|
||||
show: this.showButton("scenario"),
|
||||
titleColor: "#606266",
|
||||
titleBgColor: "#F4F4F5",
|
||||
icon: "movie",
|
||||
|
@ -547,21 +549,32 @@
|
|||
if (arr[i].hashTree != undefined && arr[i].hashTree.length > 0) {
|
||||
this.recursiveSorting(arr[i].hashTree);
|
||||
}
|
||||
// 添加debug结果
|
||||
if (this.debugResult && this.debugResult.get(arr[i].id)) {
|
||||
arr[i].requestResult = this.debugResult.get(arr[i].id);
|
||||
}
|
||||
}
|
||||
},
|
||||
sort() {
|
||||
for (let i in this.scenarioDefinition) {
|
||||
// 排序
|
||||
this.scenarioDefinition[i].index = Number(i) + 1;
|
||||
// 设置循环控制
|
||||
if (this.scenarioDefinition[i].type === ELEMENT_TYPE.LoopController && this.scenarioDefinition[i].hashTree
|
||||
&& this.scenarioDefinition[i].hashTree.length > 1) {
|
||||
this.scenarioDefinition[i].countController.proceed = true;
|
||||
}
|
||||
// 设置项目ID
|
||||
if (!this.scenarioDefinition[i].projectId) {
|
||||
this.scenarioDefinition.projectId = getCurrentProjectID();
|
||||
this.scenarioDefinition[i].projectId = getCurrentProjectID();
|
||||
}
|
||||
if (this.scenarioDefinition[i].hashTree != undefined && this.scenarioDefinition[i].hashTree.length > 0) {
|
||||
this.recursiveSorting(this.scenarioDefinition[i].hashTree);
|
||||
}
|
||||
// 添加debug结果
|
||||
if (this.debugResult && this.debugResult.get(this.scenarioDefinition[i].id)) {
|
||||
this.scenarioDefinition[i].requestResult = this.debugResult.get(this.scenarioDefinition[i].id);
|
||||
}
|
||||
}
|
||||
},
|
||||
addCustomizeApi(request) {
|
||||
|
@ -575,6 +588,7 @@
|
|||
this.customizeRequest = {};
|
||||
this.sort();
|
||||
this.reload();
|
||||
this.initProjectIds();
|
||||
},
|
||||
addScenario(arr) {
|
||||
if (arr && arr.length > 0) {
|
||||
|
@ -677,7 +691,7 @@
|
|||
}
|
||||
const index = hashTree.findIndex(d => d.resourceId === row.resourceId);
|
||||
if (index != -1) {
|
||||
hashTree.splice(index+1, 0, obj);
|
||||
hashTree.splice(index + 1, 0, obj);
|
||||
} else {
|
||||
hashTree.push(obj);
|
||||
}
|
||||
|
@ -1019,11 +1033,16 @@
|
|||
// 加载环境配置
|
||||
this.$nextTick(() => {
|
||||
this.projectIds.clear();
|
||||
this.scenarioDefinition.forEach(data=>{
|
||||
this.scenarioDefinition.forEach(data => {
|
||||
let arr = jsonPath.query(data, "$..projectId");
|
||||
arr.forEach(a => this.projectIds.add(a));
|
||||
})
|
||||
})
|
||||
},
|
||||
detailRefresh(result) {
|
||||
// 把执行结果分发给各个请求
|
||||
this.debugResult = result;
|
||||
this.sort()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,7 +112,17 @@ export default {
|
|||
}
|
||||
})
|
||||
} else {
|
||||
// 如果有环境,检查环境
|
||||
if (this.envMap && this.envMap.size > 0) {
|
||||
this.projectIds.forEach(id => {
|
||||
if (!this.envMap.get(id)) {
|
||||
sign = false;
|
||||
return false;
|
||||
}
|
||||
})
|
||||
} else {
|
||||
sign = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sign) {
|
||||
|
|
|
@ -6,8 +6,8 @@ export const ELEMENTS = new Map([
|
|||
['JDBCSampler', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
|
||||
['TCPSampler', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
|
||||
['OT_IMPORT', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
|
||||
['IfController', ["IfController","scenario", "HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler", "OT_IMPORT", "ConstantTimer", "JSR223Processor", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract", "CustomizeReq"]],
|
||||
['LoopController', ["IfController", "HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler", "OT_IMPORT", "ConstantTimer", "JSR223Processor", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract", "CustomizeReq"]],
|
||||
['IfController', ["IfController", "scenario", "HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler", "OT_IMPORT", "ConstantTimer", "JSR223Processor", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract", "CustomizeReq"]],
|
||||
['LoopController', ["IfController", "scenario", "HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler", "OT_IMPORT", "ConstantTimer", "JSR223Processor", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract", "CustomizeReq"]],
|
||||
['ConstantTimer', []],
|
||||
['JSR223Processor', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
|
||||
['JSR223PreProcessor', []],
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
<template v-slot:header>
|
||||
<ms-table-header :is-tester-permission="true" :condition.sync="condition"
|
||||
@search="initTableData" :showCreate="false"
|
||||
:title="$t('test_track.plan.test_plan')"/>
|
||||
:title="$t('test_track.plan.test_plan')">
|
||||
</ms-table-header>
|
||||
</template>
|
||||
|
||||
<env-popover :env-map="projectEnvMap" :project-ids="projectIds" @setProjectEnvMap="setProjectEnvMap"
|
||||
:project-list="projectList" ref="envPopover" class="env-popover" style="float: right; margin-top: 4px;"/>
|
||||
<el-table
|
||||
border
|
||||
class="adjust-table"
|
||||
|
@ -142,7 +144,7 @@ import MsTableOperatorButton from "../../../../common/components/MsTableOperator
|
|||
import MsTableOperator from "../../../../common/components/MsTableOperator";
|
||||
import PlanStatusTableItem from "../../../../track/common/tableItems/plan/PlanStatusTableItem";
|
||||
import PlanStageTableItem from "../../../../track/common/tableItems/plan/PlanStageTableItem";
|
||||
import {checkoutTestManagerOrTestUser} from "@/common/js/utils";
|
||||
import {checkoutTestManagerOrTestUser, strMapToObj} from "@/common/js/utils";
|
||||
import TestReportTemplateList from "../../../../track/plan/view/comonents/TestReportTemplateList";
|
||||
import TestCaseReportView from "../../../../track/plan/view/comonents/report/TestCaseReportView";
|
||||
import MsDeleteConfirm from "../../../../common/components/MsDeleteConfirm";
|
||||
|
@ -150,6 +152,7 @@ import {TEST_PLAN_CONFIGS} from "../../../../common/components/search/search-com
|
|||
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
|
||||
import {getCurrentProjectID} from "../../../../../../common/js/utils";
|
||||
import {_filter, _sort} from "@/common/js/tableUtils";
|
||||
import EnvPopover from "@/business/components/api/automation/scenario/EnvPopover";
|
||||
|
||||
|
||||
export default {
|
||||
|
@ -160,7 +163,10 @@ export default {
|
|||
TestReportTemplateList,
|
||||
PlanStageTableItem,
|
||||
PlanStatusTableItem,
|
||||
MsTableOperator, MsTableOperatorButton, MsDialogFooter, MsTableHeader, MsCreateBox, MsTablePagination
|
||||
MsTableOperator, MsTableOperatorButton, MsDialogFooter, MsTableHeader, MsCreateBox, MsTablePagination, EnvPopover
|
||||
},
|
||||
props: {
|
||||
row: Set
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -187,6 +193,9 @@ export default {
|
|||
{text: this.$t('test_track.plan.system_test'), value: 'system'},
|
||||
{text: this.$t('test_track.plan.regression_test'), value: 'regression'},
|
||||
],
|
||||
projectEnvMap: new Map(),
|
||||
projectList: [],
|
||||
projectIds: new Set()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -200,22 +209,37 @@ export default {
|
|||
this.projectId = this.$route.params.projectId;
|
||||
this.isTestManagerOrTestUser = checkoutTestManagerOrTestUser();
|
||||
this.initTableData();
|
||||
this.setScenarioSelectRows(this.row);
|
||||
this.getWsProjects();
|
||||
},
|
||||
methods: {
|
||||
confirm() {
|
||||
if (this.selection.length==0) {
|
||||
this.$warning(this.$t("api_test.definition.request.test_plan_select"));
|
||||
}else{
|
||||
this.$emit('addTestPlan', this.selection);
|
||||
const sign = this.checkEnv();
|
||||
if (!sign) {
|
||||
return false;
|
||||
}
|
||||
this.$emit('addTestPlan', this.selection, this.projectEnvMap);
|
||||
}
|
||||
},
|
||||
cancel(){
|
||||
this.$emit('cancel');
|
||||
},
|
||||
setProjectEnvMap(projectEnvMap) {
|
||||
this.projectEnvMap = projectEnvMap;
|
||||
},
|
||||
select(selection) {
|
||||
this.selection = selection.map(s => s.id);
|
||||
this.$emit('selection', selection);
|
||||
},
|
||||
setScenarioSelectRows(rows) {
|
||||
this.projectIds.clear();
|
||||
rows.forEach(row => {
|
||||
row.projectIds.forEach(id => this.projectIds.add(id));
|
||||
})
|
||||
},
|
||||
initTableData() {
|
||||
if (this.planId) {
|
||||
this.condition.planId = this.planId;
|
||||
|
@ -284,6 +308,14 @@ export default {
|
|||
this.$refs.testCaseReportView.open(planId, reportId);
|
||||
}
|
||||
},
|
||||
checkEnv() {
|
||||
return this.$refs.envPopover.checkEnv();
|
||||
},
|
||||
getWsProjects() {
|
||||
this.$get("/project/listAll", res => {
|
||||
this.projectList = res.data;
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,13 +1,24 @@
|
|||
<template>
|
||||
<div>
|
||||
<div v-loading="loading">
|
||||
<el-row :gutter="10" type="flex" justify="space-between" align="middle">
|
||||
<el-col>
|
||||
<el-input :disabled="isReadOnly" v-model="jsonPath.expression" maxlength="200" size="small" show-word-limit
|
||||
:placeholder="$t('api_test.request.extract.json_path_expression')"/>
|
||||
</el-col>
|
||||
<el-col>
|
||||
<el-select v-model="jsonPath.option" class="ms-col-type" size="small" style="width:40%;margin-right: 10px" @change="reload">
|
||||
<el-option :label="$t('api_test.request.assertions.contains')" value="CONTAINS"/>
|
||||
<el-option :label="$t('api_test.request.assertions.not_contains')" value="NOT_CONTAINS"/>
|
||||
<el-option :label="$t('api_test.request.assertions.equals')" value="EQUALS"/>
|
||||
<el-option :label="$t('commons.adv_search.operators.not_equals')" value="NOT_EQUALS"/>
|
||||
<el-option label="正则匹配" value="REGEX"/>
|
||||
</el-select>
|
||||
<el-input :disabled="isReadOnly" v-model="jsonPath.expect" size="small" show-word-limit
|
||||
:placeholder="$t('api_test.request.assertions.expect')"/>
|
||||
:placeholder="$t('api_test.request.assertions.expect')" style="width: 50%"/>
|
||||
<el-tooltip placement="top" v-if="jsonPath.option === 'REGEX'">
|
||||
<div slot="content">特殊字符"$ ( ) * + . [ ] \ ^ { } |"需转义为"\ "+"特殊字符",如"\$"</div>
|
||||
<i class="el-icon-question" style="cursor: pointer"/>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
<el-col class="assertion-btn">
|
||||
<el-button :disabled="isReadOnly" type="danger" size="mini" icon="el-icon-delete" circle @click="remove" v-if="edit"/>
|
||||
|
@ -44,8 +55,15 @@
|
|||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
if (!this.jsonPath.option) {
|
||||
this.jsonPath.option = "REGEX";
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -71,6 +89,12 @@
|
|||
jsonPath.description = jsonPath.expression + " expect: " + (jsonPath.expect ? jsonPath.expect : '');
|
||||
return jsonPath;
|
||||
},
|
||||
reload() {
|
||||
this.loading = true
|
||||
this.$nextTick(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
setJSONPathDescription() {
|
||||
this.jsonPath.description = this.jsonPath.expression + " expect: " + (this.jsonPath.expect ? this.jsonPath.expect : '');
|
||||
}
|
||||
|
|
|
@ -56,21 +56,21 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import MsApiAssertionText from "./ApiAssertionText";
|
||||
import MsApiAssertionRegex from "./ApiAssertionRegex";
|
||||
import MsApiAssertionDuration from "./ApiAssertionDuration";
|
||||
import {ASSERTION_TYPE, JSONPath} from "../../model/ApiTestModel";
|
||||
import MsApiAssertionsEdit from "./ApiAssertionsEdit";
|
||||
import MsApiAssertionJsonPath from "./ApiAssertionJsonPath";
|
||||
import MsApiAssertionJsr223 from "./ApiAssertionJsr223";
|
||||
import MsApiJsonpathSuggestList from "./ApiJsonpathSuggestList";
|
||||
import MsApiAssertionXPath2 from "./ApiAssertionXPath2";
|
||||
import {getUUID} from "@/common/js/utils";
|
||||
import ApiJsonPathSuggestButton from "./ApiJsonPathSuggestButton";
|
||||
import MsApiJsonpathSuggest from "./ApiJsonpathSuggest";
|
||||
import ApiBaseComponent from "../../../automation/scenario/common/ApiBaseComponent";
|
||||
import MsApiAssertionText from "./ApiAssertionText";
|
||||
import MsApiAssertionRegex from "./ApiAssertionRegex";
|
||||
import MsApiAssertionDuration from "./ApiAssertionDuration";
|
||||
import {ASSERTION_TYPE, JSONPath} from "../../model/ApiTestModel";
|
||||
import MsApiAssertionsEdit from "./ApiAssertionsEdit";
|
||||
import MsApiAssertionJsonPath from "./ApiAssertionJsonPath";
|
||||
import MsApiAssertionJsr223 from "./ApiAssertionJsr223";
|
||||
import MsApiJsonpathSuggestList from "./ApiJsonpathSuggestList";
|
||||
import MsApiAssertionXPath2 from "./ApiAssertionXPath2";
|
||||
import {getUUID} from "@/common/js/utils";
|
||||
import ApiJsonPathSuggestButton from "./ApiJsonPathSuggestButton";
|
||||
import MsApiJsonpathSuggest from "./ApiJsonpathSuggest";
|
||||
import ApiBaseComponent from "../../../automation/scenario/common/ApiBaseComponent";
|
||||
|
||||
export default {
|
||||
export default {
|
||||
name: "MsApiAssertions",
|
||||
components: {
|
||||
ApiBaseComponent,
|
||||
|
@ -146,6 +146,11 @@ export default {
|
|||
jsonItem.expression = data.path;
|
||||
jsonItem.expect = data.value;
|
||||
jsonItem.setJSONPathDescription();
|
||||
let expect = jsonItem.expect.replaceAll('\\', "\\\\").replaceAll('(', "\\(").replaceAll(')', "\\)")
|
||||
.replaceAll('+', "\\+").replaceAll('.', "\\.").replaceAll('[', "\\[").replaceAll(']', "\\]")
|
||||
.replaceAll('?', "\\?").replaceAll('/', "\\/").replaceAll('*', "\\*")
|
||||
.replaceAll('^', "\\^").replaceAll('{', "\\{").replaceAll('}', "\\}").replaceAll('$', "\\$");
|
||||
jsonItem.expect = expect;
|
||||
this.assertions.jsonPath.push(jsonItem);
|
||||
},
|
||||
clearJson() {
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
v-permission="['test_manager','test_user','test_viewer']">
|
||||
{{ $t('commons.performance') }}
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/reportForm" v-permission="['test_manager','test_user','test_viewer']" v-if="isReport">
|
||||
<el-menu-item index="/report" v-permission="['test_manager','test_user','test_viewer']" v-if="isReport">
|
||||
{{ $t('commons.report_statistics.title') }}
|
||||
</el-menu-item>
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -178,8 +178,11 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
calculateLoadConfiguration: function (data) {
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
for (let i = 0; i < this.threadGroups.length; i++) {
|
||||
let d = data[i];
|
||||
if (!d) {
|
||||
return;
|
||||
}
|
||||
d.forEach(item => {
|
||||
switch (item.key) {
|
||||
case TARGET_LEVEL:
|
||||
|
@ -225,7 +228,7 @@ export default {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
})
|
||||
});
|
||||
this.calculateChart(this.threadGroups[i]);
|
||||
|
||||
}
|
||||
|
|
|
@ -117,6 +117,36 @@
|
|||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-form :inline="true">
|
||||
<el-form-item>
|
||||
<div>
|
||||
{{ $t('load_test.granularity') }}
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
width="400"
|
||||
trigger="hover">
|
||||
<el-table :data="granularityData">
|
||||
<el-table-column property="start" :label="$t('load_test.duration')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.start }} - {{ scope.row.end }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="granularity" :label="$t('load_test.granularity')"/>
|
||||
</el-table>
|
||||
<i slot="reference" class="el-icon-info pointer"/>
|
||||
</el-popover>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-select v-model="granularity" :placeholder="$t('commons.please_select')" size="mini" clearable>
|
||||
<el-option v-for="op in granularityData" :key="op.granularity" :label="op.granularity" :value="op.granularity"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -134,6 +164,18 @@ export default {
|
|||
domains: [],
|
||||
params: [],
|
||||
statusCodeStr: '',
|
||||
granularity: undefined,
|
||||
granularityData: [
|
||||
{start: 0, end: 100, granularity: 1},
|
||||
{start: 101, end: 500, granularity: 5},
|
||||
{start: 501, end: 1000, granularity: 10},
|
||||
{start: 1001, end: 3000, granularity: 30},
|
||||
{start: 3001, end: 6000, granularity: 60},
|
||||
{start: 6001, end: 30000, granularity: 300},
|
||||
{start: 30001, end: 60000, granularity: 600},
|
||||
{start: 60001, end: 180000, granularity: 1800},
|
||||
{start: 180001, end: 360000, granularity: 3600},
|
||||
]
|
||||
}
|
||||
},
|
||||
props: {
|
||||
|
@ -166,6 +208,7 @@ export default {
|
|||
this.statusCodeStr = this.statusCode.join(',');
|
||||
this.domains = data.domains || [];
|
||||
this.params = data.params || [];
|
||||
this.granularity = data.granularity;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -252,6 +295,7 @@ export default {
|
|||
statusCode: statusCode,
|
||||
params: this.params,
|
||||
domains: this.domains,
|
||||
granularity: this.granularity,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
@ -287,4 +331,8 @@ export default {
|
|||
align: center;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
<el-row type="flex" justify="start" align="middle">
|
||||
<el-upload
|
||||
style="padding-right: 10px;"
|
||||
accept=".jar,.csv"
|
||||
accept=".jar,.csv,.json,.pdf,.jpg,.png,.jpeg,.doc,.docx,.xlsx"
|
||||
action=""
|
||||
:limit="fileNumLimit"
|
||||
multiple
|
||||
|
@ -170,7 +170,7 @@ export default {
|
|||
fileList: [],
|
||||
tableData: [],
|
||||
uploadList: [],
|
||||
metadataIdList:[],
|
||||
metadataIdList: [],
|
||||
fileNumLimit: 10,
|
||||
threadGroups: [],
|
||||
loadFileVisible: false,
|
||||
|
@ -276,7 +276,10 @@ export default {
|
|||
let self = this;
|
||||
let file = uploadResources.file;
|
||||
self.uploadList.push(file);
|
||||
|
||||
let type = file.name.substring(file.name.lastIndexOf(".") + 1);
|
||||
if (type.toLowerCase() !== 'jmx') {
|
||||
return;
|
||||
}
|
||||
let jmxReader = new FileReader();
|
||||
jmxReader.onload = (event) => {
|
||||
self.threadGroups = self.threadGroups.concat(findThreadGroup(event.target.result, file.name));
|
||||
|
|
|
@ -22,7 +22,6 @@ import {left2RightDrag, bottom2TopDrag, right2LeftDrag} from "../common/js/direc
|
|||
import JsonSchemaEditor from './components/common/json-schema/schema/index';
|
||||
import JSONPathPicker from 'vue-jsonpath-picker';
|
||||
import VueClipboard from 'vue-clipboard2'
|
||||
|
||||
Vue.use(JsonSchemaEditor);
|
||||
import VuePapaParse from 'vue-papa-parse'
|
||||
Vue.use(VuePapaParse)
|
||||
|
|
|
@ -1,13 +1,23 @@
|
|||
import {Message, MessageBox} from 'element-ui';
|
||||
import axios from "axios";
|
||||
import i18n from '../../i18n/i18n'
|
||||
import {TokenKey} from "@/common/js/constants";
|
||||
|
||||
export function registerRequestHeaders() {
|
||||
axios.interceptors.request.use(config => {
|
||||
let user = JSON.parse(localStorage.getItem(TokenKey));
|
||||
if (user && user.csrfToken) {
|
||||
config.headers['CSRF-TOKEN'] = user.csrfToken;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
install(Vue) {
|
||||
|
||||
// 登入请求不重定向
|
||||
let unRedirectUrls = new Set(['signin','ldap/signin','/signin', '/ldap/signin']);
|
||||
let unRedirectUrls = new Set(['signin', 'ldap/signin', '/signin', '/ldap/signin']);
|
||||
|
||||
if (!axios) {
|
||||
window.console.error('You have to install axios');
|
||||
|
|
|
@ -478,7 +478,8 @@ export default {
|
|||
delete_file: "The file already exists, please delete the file with the same name first!",
|
||||
thread_num: 'Concurrent users:',
|
||||
input_thread_num: 'Please enter the number of threads',
|
||||
duration: 'Duration time (seconds):',
|
||||
duration: 'Duration time (seconds)',
|
||||
granularity: 'Aggregation time (seconds)',
|
||||
input_duration: 'Please enter a duration',
|
||||
rps_limit: 'RPS Limit:',
|
||||
input_rps_limit: 'Please enter a limit',
|
||||
|
|
|
@ -475,7 +475,8 @@ export default {
|
|||
delete_file: "文件已存在,请先删除同名文件!",
|
||||
thread_num: '并发用户数:',
|
||||
input_thread_num: '请输入线程数',
|
||||
duration: '压测时长(秒):',
|
||||
duration: '压测时长(秒)',
|
||||
granularity: '聚合时间(秒)',
|
||||
input_duration: '请输入时长',
|
||||
rps_limit: 'RPS上限:',
|
||||
input_rps_limit: '请输入限制',
|
||||
|
|
|
@ -475,7 +475,8 @@ export default {
|
|||
delete_file: "文件已存在,請先刪除同名文件!",
|
||||
thread_num: '並發用戶數:',
|
||||
input_thread_num: '請輸入線程數',
|
||||
duration: '壓測時長(秒):',
|
||||
duration: '壓測時長(秒)',
|
||||
granularity: '聚合時間(秒)',
|
||||
input_duration: '請輸入時長',
|
||||
rps_limit: 'RPS上限:',
|
||||
input_rps_limit: '請輸入限制',
|
||||
|
|
Loading…
Reference in New Issue