Merge branch 'dev' of https://github.com/metersphere/server into dev
This commit is contained in:
commit
1e99e83ea4
|
@ -1,8 +1,11 @@
|
|||
package io.metersphere.api.dto;
|
||||
|
||||
import io.metersphere.api.dto.scenario.Scenario;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public class SaveAPITestRequest {
|
||||
|
@ -13,5 +16,5 @@ public class SaveAPITestRequest {
|
|||
|
||||
private String name;
|
||||
|
||||
private String scenarioDefinition;
|
||||
private List<Scenario> scenarioDefinition;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.api.dto.scenario;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class Body {
|
||||
private String type;
|
||||
private String raw;
|
||||
private List<KeyValue> kvs;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package io.metersphere.api.dto.scenario;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class KeyValue {
|
||||
private String name;
|
||||
private String value;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package io.metersphere.api.dto.scenario;
|
||||
|
||||
import io.metersphere.api.dto.scenario.assertions.Assertions;
|
||||
import io.metersphere.api.dto.scenario.extract.Extract;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class Request {
|
||||
private String name;
|
||||
private String url;
|
||||
private String method;
|
||||
private List<KeyValue> parameters;
|
||||
private List<KeyValue> headers;
|
||||
private Body body;
|
||||
private Assertions assertions;
|
||||
private Extract extract;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package io.metersphere.api.dto.scenario;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class Scenario {
|
||||
private String name;
|
||||
private String url;
|
||||
private List<KeyValue> variables;
|
||||
private List<KeyValue> headers;
|
||||
private List<Request> requests;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package io.metersphere.api.dto.scenario.assertions;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class AssertionDuration extends AssertionType {
|
||||
private long value;
|
||||
|
||||
public AssertionDuration() {
|
||||
setType(AssertionType.DURATION);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package io.metersphere.api.dto.scenario.assertions;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class AssertionRegex extends AssertionType {
|
||||
private String subject;
|
||||
private String expression;
|
||||
private String description;
|
||||
|
||||
public AssertionRegex() {
|
||||
setType(AssertionType.REGEX);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.api.dto.scenario.assertions;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AssertionType {
|
||||
public final static String REGEX = "Regex";
|
||||
public final static String DURATION = "Duration";
|
||||
public final static String TEXT = "Text";
|
||||
|
||||
private String type;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package io.metersphere.api.dto.scenario.assertions;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class Assertions {
|
||||
private List<AssertionRegex> regex;
|
||||
private AssertionDuration duration;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.api.dto.scenario.extract;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class Extract {
|
||||
private List<ExtractRegex> regex;
|
||||
private List<ExtractJSONPath> json;
|
||||
private List<ExtractXPath> xpath;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package io.metersphere.api.dto.scenario.extract;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ExtractCommon extends ExtractType {
|
||||
private String variable;
|
||||
private String value; // value: ${variable}
|
||||
private String expression;
|
||||
private String description;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.api.dto.scenario.extract;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ExtractJSONPath extends ExtractCommon {
|
||||
public ExtractJSONPath() {
|
||||
setType(ExtractType.JSON_PATH);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.api.dto.scenario.extract;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ExtractRegex extends ExtractCommon {
|
||||
public ExtractRegex() {
|
||||
setType(ExtractType.REGEX);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.api.dto.scenario.extract;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ExtractType {
|
||||
public final static String REGEX = "Regex";
|
||||
public final static String JSON_PATH = "JSONPath";
|
||||
public final static String XPATH = "XPath";
|
||||
|
||||
private String type;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.api.dto.scenario.extract;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ExtractXPath extends ExtractCommon {
|
||||
public ExtractXPath() {
|
||||
setType(ExtractType.XPATH);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package io.metersphere.api.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.api.dto.APITestResult;
|
||||
import io.metersphere.api.dto.QueryAPITestRequest;
|
||||
import io.metersphere.api.dto.SaveAPITestRequest;
|
||||
|
@ -25,6 +26,7 @@ import java.io.ByteArrayInputStream;
|
|||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -73,10 +75,17 @@ public class APITestService {
|
|||
}
|
||||
|
||||
public void copy(SaveAPITestRequest request) {
|
||||
request.setName(request.getName() + " Copy");
|
||||
try {
|
||||
checkNameExist(request);
|
||||
} catch (Exception e) {
|
||||
request.setName(request.getName() + " " + new Random().nextInt(1000));
|
||||
}
|
||||
|
||||
// copy test
|
||||
ApiTestWithBLOBs copy = get(request.getId());
|
||||
copy.setId(UUID.randomUUID().toString());
|
||||
copy.setName(copy.getName() + " Copy");
|
||||
copy.setName(request.getName());
|
||||
copy.setCreateTime(System.currentTimeMillis());
|
||||
copy.setUpdateTime(System.currentTimeMillis());
|
||||
copy.setStatus(APITestStatus.Saved.name());
|
||||
|
@ -96,6 +105,10 @@ public class APITestService {
|
|||
return apiTestMapper.selectByPrimaryKey(id);
|
||||
}
|
||||
|
||||
public List<ApiTest> getApiTestByProjectId(String projectId) {
|
||||
return extApiTestMapper.getApiTestByProjectId(projectId);
|
||||
}
|
||||
|
||||
public void delete(String testId) {
|
||||
deleteFileByTestId(testId);
|
||||
apiReportService.deleteByTestId(testId);
|
||||
|
@ -138,7 +151,7 @@ public class APITestService {
|
|||
test.setId(request.getId());
|
||||
test.setName(request.getName());
|
||||
test.setProjectId(request.getProjectId());
|
||||
test.setScenarioDefinition(request.getScenarioDefinition());
|
||||
test.setScenarioDefinition(JSONObject.toJSONString(request.getScenarioDefinition()));
|
||||
test.setUpdateTime(System.currentTimeMillis());
|
||||
test.setStatus(APITestStatus.Saved.name());
|
||||
apiTestMapper.updateByPrimaryKeySelective(test);
|
||||
|
@ -151,7 +164,7 @@ public class APITestService {
|
|||
test.setId(request.getId());
|
||||
test.setName(request.getName());
|
||||
test.setProjectId(request.getProjectId());
|
||||
test.setScenarioDefinition(request.getScenarioDefinition());
|
||||
test.setScenarioDefinition(JSONObject.toJSONString(request.getScenarioDefinition()));
|
||||
test.setCreateTime(System.currentTimeMillis());
|
||||
test.setUpdateTime(System.currentTimeMillis());
|
||||
test.setStatus(APITestStatus.Saved.name());
|
||||
|
@ -194,7 +207,4 @@ public class APITestService {
|
|||
}
|
||||
}
|
||||
|
||||
public List<ApiTest> getApiTestByProjectId(String projectId) {
|
||||
return extApiTestMapper.getApiTestByProjectId(projectId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<el-container class="test-container" v-loading="result.loading">
|
||||
<el-header>
|
||||
<el-row type="flex" align="middle">
|
||||
<el-input :disabled="isReadOnly" class="test-name" v-model="test.name" maxlength="60" :placeholder="$t('api_test.input_name')"
|
||||
<el-input :disabled="isReadOnly" class="test-name" v-model="test.name" maxlength="60"
|
||||
:placeholder="$t('api_test.input_name')"
|
||||
show-word-limit>
|
||||
<el-select :disabled="isReadOnly" class="test-project" v-model="test.projectId" slot="prepend"
|
||||
:placeholder="$t('api_test.select_project')">
|
||||
|
@ -17,7 +18,8 @@
|
|||
{{$t('commons.save')}}
|
||||
</el-button>
|
||||
|
||||
<el-button type="primary" plain v-if="!isShowRun" :disabled="isDisabled || isReadOnly" @click="saveRunTest">
|
||||
<el-button type="primary" plain v-if="!isShowRun" :disabled="isDisabled || isReadOnly"
|
||||
@click="saveRunTest">
|
||||
{{$t('load_test.save_and_run')}}
|
||||
</el-button>
|
||||
|
||||
|
@ -25,7 +27,8 @@
|
|||
{{$t('api_test.run')}}
|
||||
</el-button>
|
||||
|
||||
<el-button :disabled="isReadOnly" type="warning" plain @click="cancel">{{$t('commons.cancel')}}</el-button>
|
||||
<el-button :disabled="isReadOnly" type="warning" plain @click="cancel">{{$t('commons.cancel')}}
|
||||
</el-button>
|
||||
|
||||
<el-dropdown trigger="click" @command="handleCommand">
|
||||
<el-button class="el-dropdown-link more" icon="el-icon-more" plain/>
|
||||
|
@ -165,13 +168,7 @@
|
|||
},
|
||||
getOptions(url) {
|
||||
let formData = new FormData();
|
||||
let request = {
|
||||
id: this.test.id,
|
||||
projectId: this.test.projectId,
|
||||
name: this.test.name,
|
||||
scenarioDefinition: JSON.stringify(this.test.scenarioDefinition)
|
||||
}
|
||||
let requestJson = JSON.stringify(request);
|
||||
let requestJson = JSON.stringify(this.test);
|
||||
|
||||
formData.append('request', new Blob([requestJson], {
|
||||
type: "application/json"
|
||||
|
|
|
@ -143,7 +143,7 @@
|
|||
});
|
||||
},
|
||||
handleCopy(test) {
|
||||
this.result = this.$post("/api/copy", {id: test.id}, () => {
|
||||
this.result = this.$post("/api/copy", {projectId: test.projectId, id: test.id, name: test.name}, () => {
|
||||
this.$success(this.$t('commons.copy_success'));
|
||||
this.search();
|
||||
});
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
|
||||
<script>
|
||||
|
||||
import {ResponseTime} from "../../model/ScenarioModel";
|
||||
import {Duration} from "../../model/ScenarioModel";
|
||||
|
||||
export default {
|
||||
name: "MsApiAssertionResponseTime",
|
||||
name: "MsApiAssertionDuration",
|
||||
|
||||
props: {
|
||||
duration: ResponseTime,
|
||||
duration: Duration,
|
||||
value: [Number, String],
|
||||
edit: Boolean,
|
||||
callback: Function,
|
|
@ -6,14 +6,14 @@
|
|||
size="small">
|
||||
<el-option :label="$t('api_test.request.assertions.text')" :value="options.TEXT"/>
|
||||
<el-option :label="$t('api_test.request.assertions.regex')" :value="options.REGEX"/>
|
||||
<el-option :label="$t('api_test.request.assertions.response_time')" :value="options.RESPONSE_TIME"/>
|
||||
<el-option :label="$t('api_test.request.assertions.response_time')" :value="options.DURATION"/>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="20">
|
||||
<ms-api-assertion-text :is-read-only="isReadOnly" :list="assertions.regex" v-if="type === options.TEXT" :callback="after"/>
|
||||
<ms-api-assertion-regex :is-read-only="isReadOnly" :list="assertions.regex" v-if="type === options.REGEX" :callback="after"/>
|
||||
<ms-api-assertion-response-time :is-read-only="isReadOnly" v-model="time" :duration="assertions.duration"
|
||||
v-if="type === options.RESPONSE_TIME" :callback="after"/>
|
||||
<ms-api-assertion-duration :is-read-only="isReadOnly" v-model="time" :duration="assertions.duration"
|
||||
v-if="type === options.DURATION" :callback="after"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
|
@ -24,14 +24,14 @@
|
|||
<script>
|
||||
import MsApiAssertionText from "./ApiAssertionText";
|
||||
import MsApiAssertionRegex from "./ApiAssertionRegex";
|
||||
import MsApiAssertionResponseTime from "./ApiAssertionResponseTime";
|
||||
import {ASSERTION_TYPE, Assertions, ResponseTime} from "../../model/ScenarioModel";
|
||||
import MsApiAssertionDuration from "./ApiAssertionDuration";
|
||||
import {ASSERTION_TYPE, Assertions} from "../../model/ScenarioModel";
|
||||
import MsApiAssertionsEdit from "./ApiAssertionsEdit";
|
||||
|
||||
export default {
|
||||
name: "MsApiAssertions",
|
||||
|
||||
components: {MsApiAssertionsEdit, MsApiAssertionResponseTime, MsApiAssertionRegex, MsApiAssertionText},
|
||||
components: {MsApiAssertionsEdit, MsApiAssertionDuration, MsApiAssertionRegex, MsApiAssertionText},
|
||||
|
||||
props: {
|
||||
assertions: Assertions,
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<div>
|
||||
{{$t("api_test.request.assertions.response_time")}}
|
||||
</div>
|
||||
<ms-api-assertion-response-time :is-read-only="isReadOnly" v-model="assertions.duration.value" :duration="assertions.duration" :edit="true"/>
|
||||
<ms-api-assertion-duration :is-read-only="isReadOnly" v-model="assertions.duration.value" :duration="assertions.duration" :edit="true"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -21,13 +21,13 @@
|
|||
|
||||
<script>
|
||||
import MsApiAssertionRegex from "./ApiAssertionRegex";
|
||||
import MsApiAssertionResponseTime from "./ApiAssertionResponseTime";
|
||||
import MsApiAssertionDuration from "./ApiAssertionDuration";
|
||||
import {Assertions} from "../../model/ScenarioModel";
|
||||
|
||||
export default {
|
||||
name: "MsApiAssertionsEdit",
|
||||
|
||||
components: {MsApiAssertionResponseTime, MsApiAssertionRegex},
|
||||
components: {MsApiAssertionDuration, MsApiAssertionRegex},
|
||||
|
||||
props: {
|
||||
assertions: Assertions,
|
||||
|
|
|
@ -40,7 +40,7 @@ export const BODY_TYPE = {
|
|||
export const ASSERTION_TYPE = {
|
||||
TEXT: "Text",
|
||||
REGEX: "Regex",
|
||||
RESPONSE_TIME: "Response Time"
|
||||
DURATION: "Duration"
|
||||
}
|
||||
|
||||
export const ASSERTION_REGEX_SUBJECT = {
|
||||
|
@ -253,7 +253,7 @@ export class Assertions extends BaseConfig {
|
|||
|
||||
initOptions(options) {
|
||||
options = options || {};
|
||||
options.duration = new ResponseTime(options.duration);
|
||||
options.duration = new Duration(options.duration);
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
@ -291,9 +291,9 @@ export class Regex extends AssertionType {
|
|||
}
|
||||
}
|
||||
|
||||
export class ResponseTime extends AssertionType {
|
||||
export class Duration extends AssertionType {
|
||||
constructor(options) {
|
||||
super(ASSERTION_TYPE.RESPONSE_TIME);
|
||||
super(ASSERTION_TYPE.DURATION);
|
||||
this.value = undefined;
|
||||
|
||||
this.set(options);
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
<el-input v-model="form.name" autocomplete="off"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.description')" prop="description">
|
||||
<el-input v-model="form.description" autocomplete="off"/>
|
||||
<el-input v-model="form.description" autocomplete="off" type="textarea"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template v-slot:footer>
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
<el-form-item :label="$t('commons.name')" prop="name">
|
||||
<el-input v-model="form.name" autocomplete="off"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.description')">
|
||||
<el-form-item :label="$t('commons.description')" prop="description">
|
||||
<el-input type="textarea" v-model="form.description"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('workspace.organization_name')" prop="organizationId">
|
||||
|
|
Loading…
Reference in New Issue