feat(接口自动化): 场景自动化基本画面完成

This commit is contained in:
fit2-zhao 2020-12-02 19:22:58 +08:00
parent 0450186735
commit 77a1724bc7
22 changed files with 811 additions and 86 deletions

View File

@ -176,7 +176,11 @@
<artifactId>ApacheJMeter_tcp</artifactId>
<version>${jmeter.version}</version>
</dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_java</artifactId>
<version>${jmeter.version}</version>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>

View File

@ -6,6 +6,7 @@ import io.metersphere.api.dto.automation.ApiScenarioDTO;
import io.metersphere.api.dto.automation.ApiScenarioRequest;
import io.metersphere.api.dto.automation.SaveApiScenarioRequest;
import io.metersphere.api.service.ApiAutomationService;
import io.metersphere.base.domain.ApiScenario;
import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
@ -14,9 +15,8 @@ import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import javax.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping(value = "/api/automation")
@ -57,5 +57,11 @@ public class ApiAutomationController {
public void removeToGc(@RequestBody List<String> ids) {
apiAutomationService.removeToGc(ids);
}
@GetMapping("/getApiScenario/{id}")
public ApiScenario getScenarioDefinition(@PathVariable String id) {
return apiAutomationService.getApiScenario(id);
}
}

View File

@ -0,0 +1,44 @@
package io.metersphere.api.controller;
import io.metersphere.api.dto.automation.ApiApiRequest;
import io.metersphere.api.dto.automation.SaveApiTagRequest;
import io.metersphere.api.service.ApiTagService;
import io.metersphere.base.domain.ApiTag;
import io.metersphere.commons.constants.RoleConstants;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping(value = "/api/tag")
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER}, logical = Logical.OR)
public class ApiTagController {
@Resource
ApiTagService apiTagService;
@PostMapping("/list")
public List<ApiTag> list(@RequestBody ApiApiRequest request) {
return apiTagService.list(request);
}
@PostMapping(value = "/create")
public void create(@RequestBody SaveApiTagRequest request) {
apiTagService.create(request);
}
@PostMapping(value = "/update")
public void update(@RequestBody SaveApiTagRequest request) {
apiTagService.update(request);
}
@GetMapping("/delete/{id}")
public void delete(@PathVariable String id) {
apiTagService.delete(id);
}
}

View File

@ -0,0 +1,13 @@
package io.metersphere.api.dto.automation;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ApiApiRequest {
private String id;
private String projectId;
private String name;
private String userId;
}

View File

@ -0,0 +1,16 @@
package io.metersphere.api.dto.automation;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class SaveApiTagRequest {
private String id;
private String projectId;
private String userId;
private String name;
}

View File

@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
import io.metersphere.api.dto.definition.request.assertions.MsAssertions;
import io.metersphere.api.dto.definition.request.auth.MsAuthManager;
import io.metersphere.api.dto.definition.request.configurations.MsHeaderManager;
import io.metersphere.api.dto.definition.request.controller.MsIfController;
import io.metersphere.api.dto.definition.request.extract.MsExtract;
import io.metersphere.api.dto.definition.request.processors.post.MsJSR223PostProcessor;
import io.metersphere.api.dto.definition.request.processors.pre.MsJSR223PreProcessor;
@ -14,6 +15,7 @@ import io.metersphere.api.dto.definition.request.sampler.MsDubboSampler;
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
import io.metersphere.api.dto.definition.request.sampler.MsJDBCSampler;
import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler;
import io.metersphere.api.dto.definition.request.timer.MsConstantTimer;
import io.metersphere.commons.utils.LogUtil;
import lombok.Data;
import org.apache.jmeter.protocol.http.control.AuthManager;
@ -39,11 +41,13 @@ import java.util.List;
@JsonSubTypes.Type(value = MsTCPSampler.class, name = "TCPSampler"),
@JsonSubTypes.Type(value = MsDubboSampler.class, name = "DubboSampler"),
@JsonSubTypes.Type(value = MsJDBCSampler.class, name = "JDBCSampler"),
@JsonSubTypes.Type(value = MsConstantTimer.class, name = "ConstantTimer"),
@JsonSubTypes.Type(value = MsIfController.class, name = "IfController"),
})
@JSONType(seeAlso = {MsHTTPSamplerProxy.class, MsHeaderManager.class, MsJSR223PostProcessor.class,
MsJSR223PreProcessor.class, MsTestPlan.class, MsThreadGroup.class, AuthManager.class, MsAssertions.class,
MsExtract.class, MsTCPSampler.class, MsDubboSampler.class, MsJDBCSampler.class}, typeKey = "type")
MsExtract.class, MsTCPSampler.class, MsDubboSampler.class, MsJDBCSampler.class, MsConstantTimer.class, MsIfController.class}, typeKey = "type")
@Data
public abstract class MsTestElement {
private String type;

View File

@ -0,0 +1,90 @@
package io.metersphere.api.dto.definition.request.controller;
import com.alibaba.fastjson.annotation.JSONType;
import io.metersphere.api.dto.definition.request.MsTestElement;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.control.IfController;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = true)
@JSONType(typeName = "IfController")
public class MsIfController extends MsTestElement {
private String type = "IfController";
private String id;
private boolean enable = true;
private String variable;
private String operator;
private String value;
public void toHashTree(HashTree tree, List<MsTestElement> hashTree) {
final HashTree groupTree = tree.add(ifController());
if (CollectionUtils.isNotEmpty(hashTree)) {
hashTree.forEach(el -> {
el.toHashTree(groupTree, el.getHashTree());
});
}
}
private IfController ifController() {
IfController ifController = new IfController();
ifController.setEnabled(true);
ifController.setName(this.getLabel());
ifController.setCondition(this.getCondition());
ifController.setProperty(TestElement.TEST_CLASS, IfController.class.getName());
ifController.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("IfControllerPanel"));
ifController.setEvaluateAll(false);
ifController.setUseExpression(true);
return ifController;
}
public boolean isValid() {
if (StringUtils.contains(operator, "empty")) {
return StringUtils.isNotBlank(variable);
}
return StringUtils.isNotBlank(variable) && StringUtils.isNotBlank(operator) && StringUtils.isNotBlank(value);
}
public String getLabel() {
if (isValid()) {
String label = variable + " " + operator;
if (StringUtils.isNotBlank(value)) {
label += " " + this.value;
}
return label;
}
return "";
}
public String getCondition() {
String variable = "\"" + this.variable + "\"";
String operator = this.operator;
String value = "\"" + this.value + "\"";
if (StringUtils.contains(operator, "~")) {
value = "\".*" + this.value + ".*\"";
}
if (StringUtils.equals(operator, "is empty")) {
variable = "empty(" + variable + ")";
operator = "";
value = "";
}
if (StringUtils.equals(operator, "is not empty")) {
variable = "!empty(" + variable + ")";
operator = "";
value = "";
}
return "${__jexl3(" + variable + operator + value + ")}";
}
}

View File

@ -0,0 +1,45 @@
package io.metersphere.api.dto.definition.request.processors;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.annotation.JSONType;
import io.metersphere.api.dto.definition.request.MsTestElement;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.collections.CollectionUtils;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree;
import org.apache.jmeter.protocol.java.sampler.JSR223Sampler;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = true)
@JSONType(typeName = "JSR223Processor")
public class MsJSR223Processor extends MsTestElement {
private String type = "JSR223Processor";
@JSONField(ordinal = 10)
private String script;
@JSONField(ordinal = 11)
private String scriptLanguage;
public void toHashTree(HashTree tree, List<MsTestElement> hashTree) {
JSR223Sampler processor = new JSR223Sampler();
processor.setEnabled(true);
processor.setName(this.getName() + "JSR223Processor");
processor.setProperty(TestElement.TEST_CLASS, JSR223Sampler.class.getName());
processor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
processor.setProperty("cacheKey", "true");
processor.setProperty("scriptLanguage", this.getScriptLanguage());
processor.setProperty("script", this.getScript());
final HashTree jsr223PreTree = tree.add(processor);
if (CollectionUtils.isNotEmpty(hashTree)) {
hashTree.forEach(el -> {
el.toHashTree(jsr223PreTree, el.getHashTree());
});
}
}
}

View File

@ -0,0 +1,43 @@
package io.metersphere.api.dto.definition.request.timer;
import com.alibaba.fastjson.annotation.JSONType;
import io.metersphere.api.dto.definition.request.MsTestElement;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.collections.CollectionUtils;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.timers.ConstantTimer;
import org.apache.jorphan.collections.HashTree;
import java.util.List;
@Data
@EqualsAndHashCode(callSuper = true)
@JSONType(typeName = "ConstantTimer")
public class MsConstantTimer extends MsTestElement {
private String type = "ConstantTimer";
private String id;
private boolean enable = true;
private String delay;
public void toHashTree(HashTree tree, List<MsTestElement> hashTree) {
final HashTree groupTree = tree.add(constantTimer());
if (CollectionUtils.isNotEmpty(hashTree)) {
hashTree.forEach(el -> {
el.toHashTree(groupTree, el.getHashTree());
});
}
}
private ConstantTimer constantTimer() {
ConstantTimer constantTimer = new ConstantTimer();
constantTimer.setEnabled(true);
constantTimer.setName(this.getDelay() + " ms");
constantTimer.setProperty(TestElement.TEST_CLASS, ConstantTimer.class.getName());
constantTimer.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ConstantTimerGui"));
constantTimer.setDelay(this.getDelay());
return constantTimer;
}
}

View File

@ -14,10 +14,8 @@ import io.metersphere.i18n.Translator;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Objects;
import javax.annotation.Resource;
import java.util.List;
@Service
@Transactional(rollbackFor = Exception.class)
@ -110,4 +108,8 @@ public class ApiAutomationService {
MSException.throwException(Translator.get("automation_name_already_exists"));
}
}
public ApiScenario getApiScenario(String id) {
return apiScenarioMapper.selectByPrimaryKey(id);
}
}

View File

@ -0,0 +1,67 @@
package io.metersphere.api.service;
import io.metersphere.api.dto.automation.ApiApiRequest;
import io.metersphere.api.dto.automation.SaveApiTagRequest;
import io.metersphere.base.domain.ApiTag;
import io.metersphere.base.domain.ApiTagExample;
import io.metersphere.base.mapper.ApiTagMapper;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.SessionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
@Service
@Transactional(rollbackFor = Exception.class)
public class ApiTagService {
@Resource
private ApiTagMapper apiTagMapper;
public List<ApiTag> list(ApiApiRequest request) {
ApiTagExample example = new ApiTagExample();
example.createCriteria().andProjectIdEqualTo(request.getProjectId());
return apiTagMapper.selectByExample(example);
}
public void create(SaveApiTagRequest request) {
checkNameExist(request);
final ApiTag apiTag = new ApiTag();
apiTag.setId(request.getId());
apiTag.setName(request.getName());
apiTag.setProjectId(request.getProjectId());
apiTag.setCreateTime(System.currentTimeMillis());
apiTag.setUpdateTime(System.currentTimeMillis());
if (request.getUserId() == null) {
apiTag.setUserId(SessionUtils.getUserId());
} else {
apiTag.setUserId(request.getUserId());
}
apiTagMapper.insert(apiTag);
}
public void update(SaveApiTagRequest request) {
checkNameExist(request);
final ApiTag apiTag = new ApiTag();
apiTag.setId(request.getId());
apiTag.setName(request.getName());
apiTag.setProjectId(request.getProjectId());
apiTag.setUpdateTime(System.currentTimeMillis());
apiTag.setUserId(request.getUserId());
apiTagMapper.updateByPrimaryKeySelective(apiTag);
}
public void delete(String id) {
apiTagMapper.deleteByPrimaryKey(id);
}
private void checkNameExist(SaveApiTagRequest request) {
ApiTagExample example = new ApiTagExample();
example.createCriteria().andNameEqualTo(request.getName()).andProjectIdEqualTo(request.getProjectId())
.andIdNotEqualTo(request.getId());
if (apiTagMapper.countByExample(example) > 0) {
MSException.throwException("名称不能重复");
}
}
}

View File

@ -1,8 +1,7 @@
package io.metersphere.base.domain;
import lombok.Data;
import java.io.Serializable;
import lombok.Data;
@Data
public class ApiTag implements Serializable {

View File

@ -0,0 +1,31 @@
package io.metersphere.base.mapper;
import io.metersphere.base.domain.ApiTag;
import io.metersphere.base.domain.ApiTagExample;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ApiTagMapper {
long countByExample(ApiTagExample example);
int deleteByExample(ApiTagExample example);
int deleteByPrimaryKey(String id);
int insert(ApiTag record);
int insertSelective(ApiTag record);
List<ApiTag> selectByExample(ApiTagExample example);
ApiTag selectByPrimaryKey(String id);
int updateByExampleSelective(@Param("record") ApiTag record, @Param("example") ApiTagExample example);
int updateByExample(@Param("record") ApiTag record, @Param("example") ApiTagExample example);
int updateByPrimaryKeySelective(ApiTag record);
int updateByPrimaryKey(ApiTag record);
}

View File

@ -0,0 +1,228 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.base.mapper.ApiTagMapper">
<resultMap id="BaseResultMap" type="io.metersphere.base.domain.ApiTag">
<id column="id" jdbcType="VARCHAR" property="id" />
<result column="project_id" jdbcType="VARCHAR" property="projectId" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="user_id" jdbcType="VARCHAR" property="userId" />
<result column="create_time" jdbcType="BIGINT" property="createTime" />
<result column="update_time" jdbcType="BIGINT" property="updateTime" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
<foreach collection="oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Update_By_Example_Where_Clause">
<where>
<foreach collection="example.oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Base_Column_List">
id, project_id, `name`, user_id, create_time, update_time
</sql>
<select id="selectByExample" parameterType="io.metersphere.base.domain.ApiTagExample" resultMap="BaseResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List" />
from api_tag
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from api_tag
where id = #{id,jdbcType=VARCHAR}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
delete from api_tag
where id = #{id,jdbcType=VARCHAR}
</delete>
<delete id="deleteByExample" parameterType="io.metersphere.base.domain.ApiTagExample">
delete from api_tag
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</delete>
<insert id="insert" parameterType="io.metersphere.base.domain.ApiTag">
insert into api_tag (id, project_id, `name`,
user_id, create_time, update_time
)
values (#{id,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
#{userId,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT}
)
</insert>
<insert id="insertSelective" parameterType="io.metersphere.base.domain.ApiTag">
insert into api_tag
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="projectId != null">
project_id,
</if>
<if test="name != null">
`name`,
</if>
<if test="userId != null">
user_id,
</if>
<if test="createTime != null">
create_time,
</if>
<if test="updateTime != null">
update_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=VARCHAR},
</if>
<if test="projectId != null">
#{projectId,jdbcType=VARCHAR},
</if>
<if test="name != null">
#{name,jdbcType=VARCHAR},
</if>
<if test="userId != null">
#{userId,jdbcType=VARCHAR},
</if>
<if test="createTime != null">
#{createTime,jdbcType=BIGINT},
</if>
<if test="updateTime != null">
#{updateTime,jdbcType=BIGINT},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.base.domain.ApiTagExample" resultType="java.lang.Long">
select count(*) from api_tag
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</select>
<update id="updateByExampleSelective" parameterType="map">
update api_tag
<set>
<if test="record.id != null">
id = #{record.id,jdbcType=VARCHAR},
</if>
<if test="record.projectId != null">
project_id = #{record.projectId,jdbcType=VARCHAR},
</if>
<if test="record.name != null">
`name` = #{record.name,jdbcType=VARCHAR},
</if>
<if test="record.userId != null">
user_id = #{record.userId,jdbcType=VARCHAR},
</if>
<if test="record.createTime != null">
create_time = #{record.createTime,jdbcType=BIGINT},
</if>
<if test="record.updateTime != null">
update_time = #{record.updateTime,jdbcType=BIGINT},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByExample" parameterType="map">
update api_tag
set id = #{record.id,jdbcType=VARCHAR},
project_id = #{record.projectId,jdbcType=VARCHAR},
`name` = #{record.name,jdbcType=VARCHAR},
user_id = #{record.userId,jdbcType=VARCHAR},
create_time = #{record.createTime,jdbcType=BIGINT},
update_time = #{record.updateTime,jdbcType=BIGINT}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.base.domain.ApiTag">
update api_tag
<set>
<if test="projectId != null">
project_id = #{projectId,jdbcType=VARCHAR},
</if>
<if test="name != null">
`name` = #{name,jdbcType=VARCHAR},
</if>
<if test="userId != null">
user_id = #{userId,jdbcType=VARCHAR},
</if>
<if test="createTime != null">
create_time = #{createTime,jdbcType=BIGINT},
</if>
<if test="updateTime != null">
update_time = #{updateTime,jdbcType=BIGINT},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.base.domain.ApiTag">
update api_tag
set project_id = #{projectId,jdbcType=VARCHAR},
`name` = #{name,jdbcType=VARCHAR},
user_id = #{userId,jdbcType=VARCHAR},
create_time = #{createTime,jdbcType=BIGINT},
update_time = #{updateTime,jdbcType=BIGINT}
where id = #{id,jdbcType=VARCHAR}
</update>
</mapper>

View File

@ -1106,7 +1106,7 @@ sampleresult.default.encoding=UTF-8
# Netscape HTTP Cookie file
cookies=cookies
# Ability to switch to Nashorn as default Javascript Engine used by IfController and __javaScript function
# Ability to switch to Nashorn as default Javascript Engine used by MsIfController and __javaScript function
# JMeter works as following:
# - JDK >= 8 and javascript.use_rhino=false or not set : Nashorn
# - JDK >= 8 and javascript.use_rhino=true: Rhino

View File

@ -21,7 +21,7 @@
:name="item.name"
closable>
<div class="ms-api-scenario-div">
{{ item.name }}
<ms-edit-api-scenario :current-project="currentProject" :currentScenario="currentScenario" :moduleOptions="moduleOptions"/>
</div>
</el-tab-pane>
@ -43,10 +43,11 @@ import MsMainContainer from "@/business/components/common/components/MsMainConta
import MsApiScenarioList from "@/business/components/api/automation/scenario/ApiScenarioList";
import {getUUID} from "@/common/js/utils";
import MsApiScenarioModule from "@/business/components/api/automation/scenario/ApiScenarioModule";
import MsEditApiScenario from "./scenario/EditApiScenario";
export default {
name: "ApiAutomation",
components: {MsApiScenarioModule, MsApiScenarioList, MsMainContainer, MsAsideContainer, MsContainer},
components: {MsApiScenarioModule, MsApiScenarioList, MsMainContainer, MsAsideContainer, MsContainer,MsEditApiScenario},
comments: {},
data() {
return {
@ -104,6 +105,7 @@ export default {
},
editScenario(row) {
this.currentScenario = row;
console.log(row)
this.addTab({name: 'add'});
},
}

View File

@ -0,0 +1,78 @@
<template>
<el-dialog :close-on-click-modal="false" :title="$t('api_test.definition.request.title')" :visible.sync="visible"
width="45%"
:destroy-on-close="true">
<el-form :model="tagForm" label-position="right" label-width="80px" size="small" :rules="rule"
ref="tagForm">
<el-form-item :label="$t('commons.name')" prop="name">
<el-input v-model="tagForm.name" autocomplete="off" :placeholder="$t('commons.name')"/>
</el-form-item>
</el-form>
<template v-slot:footer>
<ms-dialog-footer
@cancel="visible = false"
@confirm="saveTag">
</ms-dialog-footer>
</template>
</el-dialog>
</template>
<script>
import {WORKSPACE_ID} from '@/common/js/constants';
import {getCurrentUser, getUUID} from "@/common/js/utils";
import MsDialogFooter from "@/business/components/common/components/MsDialogFooter";
export default {
name: "MsAddTag",
components: {MsDialogFooter},
props: {},
data() {
return {
tagForm: {},
visible: false,
currentModule: {},
projectId: "",
userOptions: [],
rule: {
name: [
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
{max: 50, message: this.$t('test_track.length_less_than') + '50', trigger: 'blur'}
],
principal: [{
required: true,
message: this.$t('api_test.automation.scenario.select_principal'),
trigger: 'change'
}],
},
}
}
,
methods: {
saveTag() {
this.$refs['tagForm'].validate((valid) => {
if (valid) {
let path = "/api/tag/create";
this.setParameter();
this.result = this.$post(path, this.tagForm, () => {
this.visible = false;
this.$emit('refreshTags');
});
} else {
return false;
}
})
},
setParameter() {
this.tagForm.id = getUUID();
this.tagForm.projectId = this.projectId;
},
open(projectId) {
this.projectId = projectId;
this.visible = true;
}
}
}
</script>

View File

@ -3,10 +3,10 @@
<el-card>
<el-row>
<div class="el-step__icon is-text ms-api-col" v-if="request.referenced">
<div class="el-step__icon-inner">{{request.$treeNodeId}}</div>
<div class="el-step__icon-inner">{{request.index}}</div>
</div>
<div class="el-step__icon is-text ms-api-col-create" v-else>
<div class="el-step__icon-inner">{{request.$treeNodeId}}</div>
<div class="el-step__icon-inner">{{request.index}}</div>
</div>
<i class="icon el-icon-arrow-right" :class="{'is-active': request.active}"

View File

@ -118,7 +118,7 @@ export default {
console.log(selection)
},
edit(row) {
this.$emit('edit', row);
},
execute(row) {

View File

@ -3,7 +3,7 @@
<el-card>
<el-row>
<div class="el-step__icon is-text ms-api-col" style="float: left">
<div class="el-step__icon-inner">{{timer.$treeNodeId}}</div>
<div class="el-step__icon-inner">{{timer.index}}</div>
</div>
<div>
<el-row :gutter="10" type="flex" align="middle">

View File

@ -1,18 +1,26 @@
<template>
<el-card class="card-content">
<div style="background-color: white;">
<p class="tip">{{$t('test_track.plan_view.base_info')}} </p>
<el-form :model="basicForm" label-position="right" label-width="80px" size="small" :rules="rules" ref="basicForm" style="margin-right: 20px">
<el-row>
<el-col>
<!--操作按钮-->
<div style="float: right;margin-right: 20px">
<el-button type="primary" size="small" @click="editScenario">{{$t('commons.save')}}</el-button>
</div>
</el-col>
</el-row>
<div class="tip">{{$t('test_track.plan_view.base_info')}}</div>
<el-form :model="currentScenario" label-position="right" label-width="80px" size="small" :rules="rules" ref="currentScenario" style="margin-right: 20px">
<!-- 基础信息 -->
<el-row>
<el-col :span="12">
<el-form-item :label="$t('commons.name')" prop="name">
<el-input class="ms-scenario-input" size="small" v-model="basicForm.name"/>
<el-input class="ms-scenario-input" size="small" v-model="currentScenario.name"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('test_track.module.module')" prop="moduleId">
<el-select class="ms-scenario-input" size="small" v-model="basicForm.moduleId">
<el-form-item :label="$t('test_track.module.module')" prop="apiScenarioModuleId">
<el-select class="ms-scenario-input" size="small" v-model="currentScenario.apiScenarioModuleId">
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/>
</el-select>
</el-form-item>
@ -21,14 +29,14 @@
<el-row>
<el-col :span="12">
<el-form-item :label="$t('commons.status')" prop="status">
<el-select class="ms-scenario-input" size="small" v-model="basicForm.status">
<el-select class="ms-scenario-input" size="small" v-model="currentScenario.status">
<el-option v-for="item in options" :key="item.id" :label="item.label" :value="item.id"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('api_test.definition.request.responsible')" prop="principal">
<el-select v-model="basicForm.principal"
<el-select v-model="currentScenario.principal"
:placeholder="$t('api_test.definition.request.responsible')" filterable size="small"
class="ms-scenario-input">
<el-option
@ -46,14 +54,14 @@
<el-row>
<el-col :span="12">
<el-form-item :label="$t('test_track.case.priority')" prop="level">
<el-select class="ms-scenario-input" size="small" v-model="basicForm.level">
<el-select class="ms-scenario-input" size="small" v-model="currentScenario.level">
<el-option v-for="item in options" :key="item.id" :label="item.label" :value="item.id"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('api_test.automation.follow_people')" prop="followPeople">
<el-select v-model="basicForm.followPeople"
<el-select v-model="currentScenario.followPeople"
:placeholder="$t('api_test.automation.follow_people')" filterable size="small"
class="ms-scenario-input">
<el-option
@ -71,10 +79,10 @@
<el-row>
<el-col :span="12">
<el-form-item label="Tag" prop="tagId">
<el-select v-model="basicForm.tagId" size="small" class="ms-scenario-input" placeholder="Tag"
<el-select v-model="currentScenario.tagId" size="small" class="ms-scenario-input" placeholder="Tag"
@change="tagChange" :multiple="true">
<el-option
v-for="item in maintainerOptions"
v-for="item in tags"
:key="item.id"
:label="item.id + ' (' + item.name + ')'"
:value="item.id"/>
@ -83,7 +91,7 @@
</el-button>
<template v-slot:empty>
<div>
<el-button size="mini" type="primary" @click="closeTagConfig" class="ms-scenario-button">
<el-button size="mini" type="primary" @click="openTagConfig" class="ms-scenario-button">
{{ $t('api_test.automation.create_tag') }}
</el-button>
</div>
@ -95,13 +103,14 @@
<el-col :span="12">
<el-form-item :label="$t('commons.description')" prop="description">
<el-input class="ms-http-textarea"
v-model="basicForm.description"
v-model="currentScenario.description"
type="textarea"
:autosize="{ minRows: 2, maxRows: 10}"
:rows="2" size="small"/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 场景步骤-->
@ -109,20 +118,21 @@
<p class="tip">{{$t('api_test.automation.scenario_step')}} </p>
<el-row>
<el-col :span="21">
<!-- 调试部分 -->
<div style="margin-left: 20px;border:1px #DCDFE6 solid;border-radius: 4px;margin-right: 10px">
<el-row style="margin: 5px">
<el-col :span="6" class="ms-col-one">
{{basicForm.name ===undefined || ''? $t('api_test.scenario.name') : basicForm.name}}
{{currentScenario.name ===undefined || ''? $t('api_test.scenario.name') : currentScenario.name}}
</el-col>
<el-col :span="5" class="ms-col-one">
{{$t('api_test.automation.step_total')}}:
<el-col :span="4" class="ms-col-one">
{{$t('api_test.automation.step_total')}}:{{scenarioDefinition.length}}
</el-col>
<el-col :span="5" class="ms-col-one">
<el-col :span="4" class="ms-col-one">
{{$t('api_test.automation.scenario_total')}}:
</el-col>
<el-col :span="6">
<el-col :span="8">
{{$t('api_test.definition.request.run_env')}}:
<el-select v-model="basicForm.environmentId" size="small" class="ms-htt-width"
<el-select v-model="currentScenario.environmentId" size="small" class="ms-htt-width"
:placeholder="$t('api_test.definition.request.run_env')"
@change="environmentChange" clearable>
<el-option v-for="(environment, index) in environments" :key="index"
@ -159,7 +169,7 @@
<el-card v-if="data.type==='scenario'">
<el-row>
<div class="el-step__icon is-text ms-api-col" style="float: left">
<div class="el-step__icon-inner">{{data.$treeNodeId}}</div>
<div class="el-step__icon-inner">{{index}}</div>
</div>
<div style="margin-left: 20px;float: left"> {{data.name}}</div>
</el-row>
@ -182,7 +192,7 @@
<!--提取规则-->
<ms-api-extract @remove="remove" v-if="data.type==='Extract'" customizeStyle="margin-top: 0px" :extract="data" :node="node"/>
<!--API 导入 -->
<ms-api-component :request="data" @remove="remove" current-project="currentProject" v-if="data.type==='HTTPSamplerProxy'||'DubboSampler'||'JDBCSampler'||'TCPSampler'" :node="node"/>
<ms-api-component :request="data" @remove="remove" current-project="currentProject" v-if="data.type==='HTTPSamplerProxy'||data.type==='DubboSampler'||data.type==='JDBCSampler'||data.type==='TCPSampler'" :node="node"/>
</template>
</span>
</el-tree>
@ -242,6 +252,12 @@
<ms-api-customize :request="customizeRequest" @addCustomizeApi="addCustomizeApi" :current-project="currentProject"/>
<!--<el-button style="float: right;margin: 20px" @click="addCustomizeApi">{{$t('commons.save')}}</el-button>-->
</el-drawer>
<!-- 环境 -->
<api-environment-config ref="environmentConfig" @close="environmentConfigClose"/>
<!--TAG-->
<ms-add-tag @refreshTags="refreshTags" ref="tag"/>
</div>
</el-card>
</template>
@ -249,7 +265,6 @@
<script>
import {API_STATUS} from "../../definition/model/JsonData";
import {WORKSPACE_ID} from '@/common/js/constants';
import {createComponent} from "../../definition/components/jmeter/components";
import {Assertions, Extract, IfController, JSR223Processor, ConstantTimer} from "../../definition/model/ApiTestModel";
import MsJsr233Processor from "./Jsr233Processor";
import {parseEnvironment} from "../../definition/model/EnvironmentModel";
@ -262,14 +277,17 @@
import {ELEMENTS, ELEMENT_TYPE} from "./Setting";
import MsApiCustomize from "./ApiCustomize";
import {getUUID} from "@/common/js/utils";
import ApiEnvironmentConfig from "../../definition/components/environment/ApiEnvironmentConfig";
import MsAddTag from "./AddTag";
export default {
name: "EditApiScenario",
props: {
moduleOptions: Array,
currentProject: {}
currentProject: {},
currentScenario: {},
},
components: {MsJsr233Processor, MsConstantTimer, MsIfController, MsApiAssertions, MsApiExtract, MsApiDefinition, MsApiComponent, MsApiCustomize},
components: {ApiEnvironmentConfig, MsAddTag, MsJsr233Processor, MsConstantTimer, MsIfController, MsApiAssertions, MsApiExtract, MsApiDefinition, MsApiComponent, MsApiCustomize},
data() {
return {
props: {
@ -282,11 +300,12 @@
{max: 50, message: this.$t('test_track.length_less_than') + '50', trigger: 'blur'}
],
userId: [{required: true, message: this.$t('test_track.case.input_maintainer'), trigger: 'change'}],
moduleId: [{required: true, message: this.$t('test_track.case.input_module'), trigger: 'change'}],
apiScenarioModuleId: [{required: true, message: this.$t('test_track.case.input_module'), trigger: 'change'}],
status: [{required: true, message: this.$t('commons.please_select'), trigger: 'change'}],
},
basicForm: {},
environments: [],
tags: [],
currentEnvironment: {},
maintainerOptions: [],
value: API_STATUS[0].id,
options: API_STATUS,
@ -299,17 +318,19 @@
currentRow: {cases: [], apis: []},
selectedTreeNode: undefined,
expandedNode: [],
scenarioDefinition: []
scenarioDefinition: [],
path: "/api/automation/create",
}
},
created() {
this.operatingElements = ELEMENTS.get("ALL");
this.getMaintainerOptions();
this.getApiScenario();
this.refreshTags();
},
watch: {},
methods: {
nodeClick(e) {
console.log(e)
this.operatingElements = ELEMENTS.get(e.type);
this.selectedTreeNode = e;
},
@ -321,6 +342,22 @@
apiListImport() {
this.apiListVisible = true;
},
recursiveSorting(arr) {
for (let i in arr) {
arr[i].index = Number(i) + 1;
if (arr[i].hashTree != undefined && arr[i].hashTree.length > 0) {
this.recursiveSorting(arr[i].hashTree);
}
}
},
sort() {
for (let i in this.scenarioDefinition) {
this.scenarioDefinition[i].index = Number(i) + 1;
if (this.scenarioDefinition[i].hashTree != undefined && this.scenarioDefinition[i].hashTree.length > 0) {
this.recursiveSorting(this.scenarioDefinition[i].hashTree);
}
}
},
addComponent(type) {
switch (type) {
case ELEMENT_TYPE.IfController:
@ -358,6 +395,7 @@
default:
break;
}
this.sort();
this.reload();
},
addCustomizeApi(request) {
@ -368,6 +406,7 @@
this.scenarioDefinition.push(request);
}
this.customizeRequest = {};
this.sort();
this.reload();
},
addReferenceApi() {
@ -410,6 +449,7 @@
}
})
this.apiListVisible = false;
this.sort();
this.reload();
},
getMaintainerOptions() {
@ -426,36 +466,20 @@
this.$error(this.$t('api_test.select_project'));
return;
}
this.$refs.environmentConfig.open(this.currentProject.id);
this.$refs.tag.open(this.currentProject.id);
},
closeTagConfig() {
},
addPre() {
let jsr223PreProcessor = createComponent("JSR223PreProcessor");
this.request.hashTree.push(jsr223PreProcessor);
this.reload();
},
addPost() {
let jsr223PostProcessor = createComponent("JSR223PostProcessor");
this.request.hashTree.push(jsr223PostProcessor);
this.reload();
},
addAssertions() {
let assertions = new Assertions();
this.request.hashTree.push(assertions);
this.reload();
},
addExtract() {
let jsonPostProcessor = new Extract();
this.request.hashTree.push(jsonPostProcessor);
this.reload();
refreshTags() {
let obj = {projectId: this.currentProject.id};
this.$post('/api/tag/list', obj, response => {
this.tags = response.data;
});
},
remove(row, node) {
const parent = node.parent
const hashTree = parent.data.hashTree || parent.data;
const index = hashTree.findIndex(d => d.id != undefined && row.id != undefined && d.id === row.id)
hashTree.splice(index, 1);
this.sort();
this.reload();
},
reload() {
@ -474,22 +498,7 @@
this.environments.forEach(environment => {
parseEnvironment(environment);
});
let hasEnvironment = false;
for (let i in this.environments) {
if (this.environments[i].id === this.api.environmentId) {
this.api.environment = this.environments[i];
hasEnvironment = true;
break;
}
}
if (!hasEnvironment) {
this.api.environmentId = '';
this.api.environment = undefined;
}
});
} else {
this.api.environmentId = '';
this.api.environment = undefined;
}
},
openEnvironmentConfig() {
@ -502,7 +511,7 @@
environmentChange(value) {
for (let i in this.environments) {
if (this.environments[i].id === value) {
this.api.request.useEnvironment = this.environments[i].id;
this.currentEnvironment = this.environments[i].id;
break;
}
}
@ -517,7 +526,7 @@
return false;
},
allowDrag() {
this.sort();
},
nodeExpand(data) {
if (data.resourceId) {
@ -529,6 +538,49 @@
this.expandedNode.splice(this.expandedNode.indexOf(data.resourceId), 1);
}
},
getPath(id) {
if (id === null) {
return null;
}
let path = this.moduleOptions.filter(function (item) {
return item.id === id ? item.path : "";
});
return path[0].path;
},
editScenario() {
this.$refs['currentScenario'].validate((valid) => {
if (valid) {
this.setParameter();
this.result = this.$post(this.path, this.currentScenario, () => {
this.$success(this.$t('commons.save_success'));
this.path = "/api/automation/update";
})
}
})
},
getApiScenario() {
if (this.currentScenario.id) {
this.path = "/api/automation/update";
this.result = this.$get("/api/automation/getApiScenario/" + this.currentScenario.id, response => {
if (response.data) {
this.scenarioDefinition = JSON.parse(response.data.scenarioDefinition);
}
})
}
},
setParameter() {
this.currentScenario.projectId = this.currentProject.id;
if (!this.currentScenario.id) {
this.currentScenario.id = getUUID().substring(0, 8);
}
this.currentScenario.modulePath = this.getPath(this.currentScenario.apiScenarioModuleId);
this.currentScenario.scenarioDefinition = JSON.stringify(this.scenarioDefinition);
this.currentScenario.tagId = JSON.stringify(this.currentScenario.tagId);
if (this.currentModule != null) {
this.currentScenario.modulePath = this.currentModule.method !== undefined ? this.currentModule.method : null;
this.currentScenario.apiScenarioModuleId = this.currentModule.id;
}
},
}
}
</script>
@ -539,7 +591,7 @@
}
.ms-scenario-button {
margin-left: 45%;
margin-left: 30%;
padding: 7px;
}

View File

@ -2,7 +2,7 @@
<el-card>
<el-row>
<div class="el-step__icon is-text ms-api-col" style="float: left">
<div class="el-step__icon-inner">{{controller.$treeNodeId}}</div>
<div class="el-step__icon-inner">{{controller.index}}</div>
</div>
<div>
<el-row :gutter="10" type="flex" align="middle">
@ -37,6 +37,7 @@
props: {
controller: {},
node: {},
index: Object,
},
data() {
return {