feat(接口测试): 增加Dubbo支持
This commit is contained in:
parent
55c0a4be65
commit
390384c6a4
|
@ -121,7 +121,7 @@
|
|||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.45</version>
|
||||
<version>1.2.72</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package io.metersphere.api.dto.scenario;
|
||||
|
||||
import io.metersphere.api.dto.scenario.request.dubbo.ConfigCenter;
|
||||
import io.metersphere.api.dto.scenario.request.dubbo.ConsumerAndService;
|
||||
import io.metersphere.api.dto.scenario.request.dubbo.RegistryCenter;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DubboConfig {
|
||||
private ConfigCenter configCenter;
|
||||
private RegistryCenter registryCenter;
|
||||
private ConsumerAndService consumerAndService;
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
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 Boolean useEnvironment;
|
||||
private String path;
|
||||
private List<KeyValue> parameters;
|
||||
private List<KeyValue> headers;
|
||||
private Body body;
|
||||
private Assertions assertions;
|
||||
private Extract extract;
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package io.metersphere.api.dto.scenario;
|
||||
|
||||
import io.metersphere.api.dto.scenario.request.Request;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -12,4 +13,5 @@ public class Scenario {
|
|||
private List<KeyValue> variables;
|
||||
private List<KeyValue> headers;
|
||||
private List<Request> requests;
|
||||
private DubboConfig dubboConfig;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package io.metersphere.api.dto.scenario.request;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.alibaba.fastjson.annotation.JSONType;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.metersphere.api.dto.scenario.KeyValue;
|
||||
import io.metersphere.api.dto.scenario.assertions.Assertions;
|
||||
import io.metersphere.api.dto.scenario.extract.Extract;
|
||||
import io.metersphere.api.dto.scenario.request.dubbo.ConfigCenter;
|
||||
import io.metersphere.api.dto.scenario.request.dubbo.ConsumerAndService;
|
||||
import io.metersphere.api.dto.scenario.request.dubbo.RegistryCenter;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@JSONType(typeName = RequestType.DUBBO)
|
||||
public class DubboRequest implements Request {
|
||||
// type 必须放最前面,以便能够转换正确的类
|
||||
private String type = RequestType.DUBBO;
|
||||
@JSONField(ordinal = 1)
|
||||
private String name;
|
||||
@JSONField(ordinal = 2)
|
||||
private String protocol;
|
||||
@JsonProperty(value = "interface")
|
||||
@JSONField(ordinal = 3, name = "interface")
|
||||
private String _interface;
|
||||
@JSONField(ordinal = 4)
|
||||
private String method;
|
||||
|
||||
@JSONField(ordinal = 5)
|
||||
private ConfigCenter configCenter;
|
||||
@JSONField(ordinal = 6)
|
||||
private RegistryCenter registryCenter;
|
||||
@JSONField(ordinal = 7)
|
||||
private ConsumerAndService consumerAndService;
|
||||
|
||||
@JSONField(ordinal = 8)
|
||||
private List<KeyValue> args;
|
||||
@JSONField(ordinal = 9)
|
||||
private List<KeyValue> attachmentArgs;
|
||||
@JSONField(ordinal = 10)
|
||||
private Assertions assertions;
|
||||
@JSONField(ordinal = 11)
|
||||
private Extract extract;
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package io.metersphere.api.dto.scenario.request;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.alibaba.fastjson.annotation.JSONType;
|
||||
import io.metersphere.api.dto.scenario.Body;
|
||||
import io.metersphere.api.dto.scenario.KeyValue;
|
||||
import io.metersphere.api.dto.scenario.assertions.Assertions;
|
||||
import io.metersphere.api.dto.scenario.extract.Extract;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@JSONType(typeName = RequestType.HTTP)
|
||||
public class HttpRequest implements Request {
|
||||
// type 必须放最前面,以便能够转换正确的类
|
||||
private String type = RequestType.HTTP;
|
||||
@JSONField(ordinal = 1)
|
||||
private String name;
|
||||
@JSONField(ordinal = 2)
|
||||
private String url;
|
||||
@JSONField(ordinal = 3)
|
||||
private String method;
|
||||
@JSONField(ordinal = 4)
|
||||
private String path;
|
||||
@JSONField(ordinal = 5)
|
||||
private Boolean useEnvironment;
|
||||
@JSONField(ordinal = 6)
|
||||
private List<KeyValue> parameters;
|
||||
@JSONField(ordinal = 7)
|
||||
private List<KeyValue> headers;
|
||||
@JSONField(ordinal = 8)
|
||||
private Body body;
|
||||
@JSONField(ordinal = 9)
|
||||
private Assertions assertions;
|
||||
@JSONField(ordinal = 10)
|
||||
private Extract extract;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package io.metersphere.api.dto.scenario.request;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONType;
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type")
|
||||
@JsonSubTypes({
|
||||
@JsonSubTypes.Type(value = HttpRequest.class, name = RequestType.HTTP),
|
||||
@JsonSubTypes.Type(value = DubboRequest.class, name = RequestType.DUBBO)
|
||||
})
|
||||
@JSONType(seeAlso = {HttpRequest.class, DubboRequest.class}, typeKey = "type")
|
||||
public interface Request {
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package io.metersphere.api.dto.scenario.request;
|
||||
|
||||
public class RequestType {
|
||||
|
||||
public static final String HTTP = "HTTP";
|
||||
|
||||
public static final String DUBBO = "DUBBO";
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package io.metersphere.api.dto.scenario.request.dubbo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ConfigCenter {
|
||||
private String protocol;
|
||||
private String group;
|
||||
private String namespace;
|
||||
private String username;
|
||||
private String address;
|
||||
private String password;
|
||||
private String timeout;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package io.metersphere.api.dto.scenario.request.dubbo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ConsumerAndService {
|
||||
private String timeout;
|
||||
private String version;
|
||||
private String retries;
|
||||
private String cluster;
|
||||
private String group;
|
||||
private String connections;
|
||||
private String async;
|
||||
private String loadBalance;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package io.metersphere.api.dto.scenario.request.dubbo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RegistryCenter {
|
||||
private String protocol;
|
||||
private String group;
|
||||
private String username;
|
||||
private String address;
|
||||
private String password;
|
||||
private String timeout;
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package io.metersphere.api.parse;
|
||||
|
||||
import io.metersphere.api.dto.scenario.KeyValue;
|
||||
import io.metersphere.api.dto.scenario.Request;
|
||||
import io.metersphere.api.dto.scenario.request.HttpRequest;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -42,11 +42,11 @@ public abstract class ApiImportAbstractParser implements ApiImportParser {
|
|||
return testStr.toString();
|
||||
}
|
||||
|
||||
protected void addContentType(Request request, String contentType) {
|
||||
protected void addContentType(HttpRequest request, String contentType) {
|
||||
addHeader(request, HttpHeader.CONTENT_TYPE.toString(), contentType);
|
||||
}
|
||||
|
||||
protected void addCookie(Request request, String key, String value) {
|
||||
protected void addCookie(HttpRequest request, String key, String value) {
|
||||
List<KeyValue> headers = Optional.ofNullable(request.getHeaders()).orElse(new ArrayList<>());
|
||||
boolean hasCookie = false;
|
||||
for (KeyValue header : headers) {
|
||||
|
@ -61,7 +61,7 @@ public abstract class ApiImportAbstractParser implements ApiImportParser {
|
|||
}
|
||||
}
|
||||
|
||||
protected void addHeader(Request request, String key, String value) {
|
||||
protected void addHeader(HttpRequest request, String key, String value) {
|
||||
List<KeyValue> headers = Optional.ofNullable(request.getHeaders()).orElse(new ArrayList<>());
|
||||
boolean hasContentType = false;
|
||||
for (KeyValue header : headers) {
|
||||
|
|
|
@ -6,8 +6,9 @@ import io.metersphere.api.dto.parse.ApiImport;
|
|||
import io.metersphere.api.dto.parse.postman.*;
|
||||
import io.metersphere.api.dto.scenario.Body;
|
||||
import io.metersphere.api.dto.scenario.KeyValue;
|
||||
import io.metersphere.api.dto.scenario.Request;
|
||||
import io.metersphere.api.dto.scenario.request.HttpRequest;
|
||||
import io.metersphere.api.dto.scenario.Scenario;
|
||||
import io.metersphere.api.dto.scenario.request.Request;
|
||||
import io.metersphere.commons.constants.MsRequestBodyType;
|
||||
import io.metersphere.commons.constants.PostmanRequestBodyMode;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -60,7 +61,7 @@ public class PostmanParser extends ApiImportAbstractParser {
|
|||
List<PostmanItem> item = postmanCollection.getItem();
|
||||
List<Request> requests = new ArrayList<>();
|
||||
for (PostmanItem requestItem : item) {
|
||||
Request request = new Request();
|
||||
HttpRequest request = new HttpRequest();
|
||||
PostmanRequest requestDesc = requestItem.getRequest();
|
||||
PostmanUrl url = requestDesc.getUrl();
|
||||
request.setName(requestItem.getName());
|
||||
|
@ -75,7 +76,7 @@ public class PostmanParser extends ApiImportAbstractParser {
|
|||
return requests;
|
||||
}
|
||||
|
||||
private Body parseBody(PostmanRequest requestDesc, Request request) {
|
||||
private Body parseBody(PostmanRequest requestDesc, HttpRequest request) {
|
||||
Body body = new Body();
|
||||
JSONObject postmanBody = requestDesc.getBody();
|
||||
String bodyMode = postmanBody.getString("mode");
|
||||
|
|
|
@ -5,8 +5,9 @@ import com.alibaba.fastjson.JSONObject;
|
|||
import io.metersphere.api.dto.parse.ApiImport;
|
||||
import io.metersphere.api.dto.scenario.Body;
|
||||
import io.metersphere.api.dto.scenario.KeyValue;
|
||||
import io.metersphere.api.dto.scenario.Request;
|
||||
import io.metersphere.api.dto.scenario.request.HttpRequest;
|
||||
import io.metersphere.api.dto.scenario.Scenario;
|
||||
import io.metersphere.api.dto.scenario.request.Request;
|
||||
import io.metersphere.commons.constants.MsRequestBodyType;
|
||||
import io.metersphere.commons.constants.SwaggerParameterType;
|
||||
import io.swagger.models.*;
|
||||
|
@ -43,7 +44,7 @@ public class Swagger2Parser extends ApiImportAbstractParser {
|
|||
Set<HttpMethod> httpMethods = operationMap.keySet();
|
||||
for (HttpMethod method : httpMethods) {
|
||||
Operation operation = operationMap.get(method);
|
||||
Request request = new Request();
|
||||
HttpRequest request = new HttpRequest();
|
||||
request.setName(operation.getOperationId());
|
||||
request.setPath(pathName);
|
||||
request.setUseEnvironment(true);
|
||||
|
@ -68,13 +69,11 @@ public class Swagger2Parser extends ApiImportAbstractParser {
|
|||
|
||||
}
|
||||
}
|
||||
scenarioMap.values().forEach(scenario -> {
|
||||
scenarios.add(scenario);
|
||||
});
|
||||
scenarios.addAll(scenarioMap.values());
|
||||
return scenarios;
|
||||
}
|
||||
|
||||
private void parseParameters(Operation operation, Map<String, Model> definitions, Request request) {
|
||||
private void parseParameters(Operation operation, Map<String, Model> definitions, HttpRequest request) {
|
||||
|
||||
List<Parameter> parameters = operation.getParameters();
|
||||
|
||||
|
@ -105,17 +104,17 @@ public class Swagger2Parser extends ApiImportAbstractParser {
|
|||
}
|
||||
}
|
||||
|
||||
private void parseCookieParameters(Parameter parameter, Request request) {
|
||||
private void parseCookieParameters(Parameter parameter, HttpRequest request) {
|
||||
CookieParameter cookieParameter = (CookieParameter) parameter;
|
||||
addCookie(request, cookieParameter.getName(), cookieParameter.getDescription());
|
||||
}
|
||||
|
||||
private void parseHeaderParameters(Parameter parameter, Request request) {
|
||||
private void parseHeaderParameters(Parameter parameter, HttpRequest request) {
|
||||
HeaderParameter headerParameter = (HeaderParameter) parameter;
|
||||
addHeader(request, headerParameter.getName(), headerParameter.getDescription());
|
||||
}
|
||||
|
||||
private void parseBodyParameters(Parameter parameter, Request request, Map<String, Model> definitions) {
|
||||
private void parseBodyParameters(Parameter parameter, HttpRequest request, Map<String, Model> definitions) {
|
||||
BodyParameter bodyParameter = (BodyParameter) parameter;
|
||||
Body body = Optional.ofNullable(request.getBody()).orElse(new Body());
|
||||
body.setType(MsRequestBodyType.RAW.value());
|
||||
|
@ -167,7 +166,7 @@ public class Swagger2Parser extends ApiImportAbstractParser {
|
|||
return jsonObject;
|
||||
}
|
||||
|
||||
private void parseFormDataParameters(Parameter parameter, Request request) {
|
||||
private void parseFormDataParameters(Parameter parameter, HttpRequest request) {
|
||||
Body body = Optional.ofNullable(request.getBody()).orElse(new Body());
|
||||
body.setType(MsRequestBodyType.FORM_DATA.value());
|
||||
List<KeyValue> keyValues = Optional.ofNullable(body.getKvs()).orElse(new ArrayList<>());
|
||||
|
@ -176,7 +175,7 @@ public class Swagger2Parser extends ApiImportAbstractParser {
|
|||
request.setBody(body);
|
||||
}
|
||||
|
||||
private void parseQueryParameters(Parameter parameter, Request request) {
|
||||
private void parseQueryParameters(Parameter parameter, HttpRequest request) {
|
||||
QueryParameter queryParameter = (QueryParameter) parameter;
|
||||
List<KeyValue> parameters = Optional.ofNullable(request.getParameters()).orElse(new ArrayList<>());
|
||||
parameters.add(new KeyValue(queryParameter.getName(), "", queryParameter.getDescription()));
|
||||
|
|
|
@ -176,8 +176,8 @@
|
|||
})
|
||||
},
|
||||
cancel() {
|
||||
// console.log(this.test.toJMX().xml)
|
||||
this.$router.push('/api/test/list/all');
|
||||
console.log(this.test.toJMX().xml)
|
||||
// this.$router.push('/api/test/list/all');
|
||||
},
|
||||
getOptions(url) {
|
||||
let formData = new FormData();
|
||||
|
|
|
@ -6,15 +6,17 @@
|
|||
<div class="kv-row" v-for="(item, index) in items" :key="index">
|
||||
<el-row type="flex" :gutter="20" justify="space-between" align="middle">
|
||||
<el-col>
|
||||
<el-input v-if="!suggestions" :disabled="isReadOnly" v-model="item.name" size="small" maxlength="100" @change="change"
|
||||
:placeholder="$t('api_test.key')" show-word-limit/>
|
||||
<el-input v-if="!suggestions" :disabled="isReadOnly" v-model="item.name" size="small" maxlength="100"
|
||||
@change="change"
|
||||
:placeholder="keyText" show-word-limit/>
|
||||
<el-autocomplete :maxlength="100" v-if="suggestions" v-model="item.name" size="small"
|
||||
:fetch-suggestions="querySearch" @change="change" :placeholder="$t('api_test.key')" show-word-limit/>
|
||||
:fetch-suggestions="querySearch" @change="change" :placeholder="keyText"
|
||||
show-word-limit/>
|
||||
|
||||
</el-col>
|
||||
<el-col>
|
||||
<el-input :disabled="isReadOnly" v-model="item.value" size="small" maxlength="500" @change="change"
|
||||
:placeholder="$t('api_test.value')" show-word-limit/>
|
||||
:placeholder="valueText" show-word-limit/>
|
||||
</el-col>
|
||||
<el-col class="kv-delete">
|
||||
<el-button size="mini" class="el-icon-delete-solid" circle @click="remove(index)"
|
||||
|
@ -32,6 +34,8 @@
|
|||
name: "MsApiKeyValue",
|
||||
|
||||
props: {
|
||||
keyPlaceholder: String,
|
||||
valuePlaceholder: String,
|
||||
description: String,
|
||||
items: Array,
|
||||
isReadOnly: {
|
||||
|
@ -41,6 +45,15 @@
|
|||
suggestions: Array
|
||||
},
|
||||
|
||||
computed: {
|
||||
keyText() {
|
||||
return this.keyPlaceholder || this.$t("api_test.key");
|
||||
},
|
||||
valueText() {
|
||||
return this.valuePlaceholder || this.$t("api_test.value");
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
remove: function (index) {
|
||||
this.items.splice(index, 1);
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
<ms-api-request-config :is-read-only="isReadOnly" :scenario="scenario" :open="select"/>
|
||||
<ms-api-request-config :is-read-only="isReadOnly" :scenario="scenario" @select="select"/>
|
||||
</ms-api-collapse-item>
|
||||
</draggable>
|
||||
</ms-api-collapse>
|
||||
|
@ -36,7 +36,7 @@
|
|||
<el-main class="scenario-main">
|
||||
<div class="scenario-form">
|
||||
<ms-api-scenario-form :is-read-only="isReadOnly" :scenario="selected" :project-id="projectId" v-if="isScenario"/>
|
||||
<ms-api-request-form :is-read-only="isReadOnly" :request="selected" :project-id="projectId" v-if="isRequest"/>
|
||||
<ms-api-request-form :is-read-only="isReadOnly" :request="selected" v-if="isRequest"/>
|
||||
</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
|
@ -46,8 +46,8 @@
|
|||
|
||||
import MsApiCollapseItem from "./collapse/ApiCollapseItem";
|
||||
import MsApiCollapse from "./collapse/ApiCollapse";
|
||||
import MsApiRequestConfig from "./ApiRequestConfig";
|
||||
import MsApiRequestForm from "./ApiRequestForm";
|
||||
import MsApiRequestConfig from "./request/ApiRequestConfig";
|
||||
import MsApiRequestForm from "./request/ApiRequestForm";
|
||||
import MsApiScenarioForm from "./ApiScenarioForm";
|
||||
import {Scenario, Request} from "../model/ScenarioModel";
|
||||
import draggable from 'vuedraggable';
|
||||
|
|
|
@ -5,12 +5,19 @@
|
|||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('api_test.environment.environment')">
|
||||
<el-select :disabled="isReadOnly" v-model="scenario.environmentId" class="environment-select" @change="environmentChange" clearable>
|
||||
<el-option v-for="(environment, index) in environments" :key="index" :label="environment.name + ': ' + environment.protocol + '://' + environment.socket" :value="environment.id"/>
|
||||
<el-button class="environment-button" size="mini" type="primary" @click="openEnvironmentConfig">{{$t('api_test.environment.environment_config')}}</el-button>
|
||||
<el-select :disabled="isReadOnly" v-model="scenario.environmentId" class="environment-select"
|
||||
@change="environmentChange" clearable>
|
||||
<el-option v-for="(environment, index) in environments" :key="index"
|
||||
:label="environment.name + ': ' + environment.protocol + '://' + environment.socket"
|
||||
:value="environment.id"/>
|
||||
<el-button class="environment-button" size="mini" type="primary" @click="openEnvironmentConfig">
|
||||
{{$t('api_test.environment.environment_config')}}
|
||||
</el-button>
|
||||
<template v-slot:empty>
|
||||
<div class="empty-environment">
|
||||
<el-button class="environment-button" size="mini" type="primary" @click="openEnvironmentConfig">{{$t('api_test.environment.environment_config')}}</el-button>
|
||||
<el-button class="environment-button" size="mini" type="primary" @click="openEnvironmentConfig">
|
||||
{{$t('api_test.environment.environment_config')}}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-select>
|
||||
|
@ -18,10 +25,20 @@
|
|||
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane :label="$t('api_test.scenario.variables')" name="parameters">
|
||||
<ms-api-scenario-variables :is-read-only="isReadOnly" :items="scenario.variables" :description="$t('api_test.scenario.kv_description')"/>
|
||||
<ms-api-scenario-variables :is-read-only="isReadOnly" :items="scenario.variables"
|
||||
:description="$t('api_test.scenario.kv_description')"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('api_test.scenario.headers')" name="headers">
|
||||
<ms-api-key-value :is-read-only="isReadOnly" :items="scenario.headers" :suggestions="headerSuggestions" :description="$t('api_test.scenario.kv_description')"/>
|
||||
<ms-api-key-value :is-read-only="isReadOnly" :items="scenario.headers" :suggestions="headerSuggestions"
|
||||
:description="$t('api_test.scenario.kv_description')"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('api_test.scenario.dubbo')" name="dubbo">
|
||||
<div class="dubbo-config-title">Config Center</div>
|
||||
<ms-dubbo-config-center :config="scenario.dubboConfig.configCenter"/>
|
||||
<div class="dubbo-config-title">Registry Center</div>
|
||||
<ms-dubbo-registry-center :registry="scenario.dubboConfig.registryCenter"/>
|
||||
<div class="dubbo-config-title">Consumer & Service</div>
|
||||
<ms-dubbo-consumer-service :consumer="scenario.dubboConfig.consumerAndService"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
|
@ -36,11 +53,17 @@
|
|||
import {Scenario} from "../model/ScenarioModel";
|
||||
import MsApiScenarioVariables from "./ApiScenarioVariables";
|
||||
import ApiEnvironmentConfig from "./ApiEnvironmentConfig";
|
||||
import {requestHeaders} from "../../../../../common/js/constants";
|
||||
import {REQUEST_HEADERS} from "@/common/js/constants";
|
||||
import MsDubboRegistryCenter from "@/business/components/api/test/components/request/dubbo/RegistryCenter";
|
||||
import MsDubboConfigCenter from "@/business/components/api/test/components/request/dubbo/ConfigCenter";
|
||||
import MsDubboConsumerService from "@/business/components/api/test/components/request/dubbo/ConsumerAndService";
|
||||
|
||||
export default {
|
||||
name: "MsApiScenarioForm",
|
||||
components: {ApiEnvironmentConfig, MsApiScenarioVariables, MsApiKeyValue},
|
||||
components: {
|
||||
MsDubboConsumerService,
|
||||
MsDubboConfigCenter, MsDubboRegistryCenter, ApiEnvironmentConfig, MsApiScenarioVariables, MsApiKeyValue
|
||||
},
|
||||
props: {
|
||||
scenario: Scenario,
|
||||
projectId: String,
|
||||
|
@ -65,7 +88,7 @@
|
|||
{max: 100, message: this.$t('commons.input_limit', [1, 100]), trigger: 'blur'}
|
||||
]
|
||||
},
|
||||
headerSuggestions: requestHeaders
|
||||
headerSuggestions: REQUEST_HEADERS
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -143,7 +166,13 @@
|
|||
}
|
||||
|
||||
.empty-environment {
|
||||
padding: 10px 0px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.dubbo-config-title {
|
||||
margin-bottom: 10px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -24,5 +24,7 @@
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.request-method-select {
|
||||
width: 110px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
import MsApiScenarioVariables from "../ApiScenarioVariables";
|
||||
import MsApiKeyValue from "../ApiKeyValue";
|
||||
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
|
||||
import {requestHeaders} from "../../../../../../common/js/constants";
|
||||
import {REQUEST_HEADERS} from "../../../../../../common/js/constants";
|
||||
|
||||
export default {
|
||||
name: "EnvironmentEdit",
|
||||
|
@ -71,7 +71,7 @@
|
|||
],
|
||||
socket :[{required: true, validator: socketValidator, trigger: 'blur'}],
|
||||
},
|
||||
headerSuggestions: requestHeaders
|
||||
headerSuggestions: REQUEST_HEADERS
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
<template>
|
||||
<el-form :model="request" :rules="rules" ref="request" label-width="100px" :disabled="isReadOnly">
|
||||
|
||||
<el-form-item :label="$t('api_test.request.name')" prop="name">
|
||||
<el-input v-model="request.name" maxlength="300" show-word-limit/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('api_test.request.dubbo.protocol')" prop="protocol">
|
||||
<el-select v-model="request.protocol">
|
||||
<el-option label="dubbo://" :value="protocols.DUBBO"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane label="Interface" name="interface">
|
||||
<ms-dubbo-interface :request="request"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="Config Center" name="config">
|
||||
<ms-dubbo-config-center :config="request.configCenter"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="Registry Center" name="registry">
|
||||
<ms-dubbo-registry-center :registry="request.registryCenter"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="Consumer & Service" name="consumer">
|
||||
<ms-dubbo-consumer-service :consumer="request.consumerAndService"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<el-tabs v-model="activeName2">
|
||||
<el-tab-pane label="Args" name="args">
|
||||
<ms-api-key-value :is-read-only="isReadOnly" :items="request.args"
|
||||
key-placeholder="Param Type" value-placeholder="Param Value"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="Attachment Args" name="attachment">
|
||||
<ms-api-key-value :is-read-only="isReadOnly" :items="request.attachmentArgs"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('api_test.request.assertions.label')" name="assertions">
|
||||
<ms-api-assertions :is-read-only="isReadOnly" :assertions="request.assertions"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('api_test.request.extract.label')" name="extract">
|
||||
<ms-api-extract :is-read-only="isReadOnly" :extract="request.extract"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsApiKeyValue from "../ApiKeyValue";
|
||||
import MsApiBody from "../ApiBody";
|
||||
import MsApiAssertions from "../assertion/ApiAssertions";
|
||||
import {DubboRequest} from "../../model/ScenarioModel";
|
||||
import MsApiExtract from "../extract/ApiExtract";
|
||||
import ApiRequestMethodSelect from "../collapse/ApiRequestMethodSelect";
|
||||
import MsDubboInterface from "@/business/components/api/test/components/request/dubbo/Interface";
|
||||
import MsDubboRegistryCenter from "@/business/components/api/test/components/request/dubbo/RegistryCenter";
|
||||
import MsDubboConfigCenter from "@/business/components/api/test/components/request/dubbo/ConfigCenter";
|
||||
import MsDubboConsumerService from "@/business/components/api/test/components/request/dubbo/ConsumerAndService";
|
||||
|
||||
export default {
|
||||
name: "MsApiDubboRequestForm",
|
||||
components: {
|
||||
MsDubboConsumerService,
|
||||
MsDubboConfigCenter,
|
||||
MsDubboRegistryCenter,
|
||||
MsDubboInterface, ApiRequestMethodSelect, MsApiExtract, MsApiAssertions, MsApiBody, MsApiKeyValue
|
||||
},
|
||||
props: {
|
||||
request: DubboRequest,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
activeName: "interface",
|
||||
activeName2: "args",
|
||||
protocols: DubboRequest.PROTOCOLS,
|
||||
rules: {
|
||||
name: [
|
||||
{max: 300, message: this.$t('commons.input_limit', [1, 300]), trigger: 'blur'}
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
useEnvironmentChange(value) {
|
||||
if (value && !this.request.environment) {
|
||||
this.$error(this.$t('api_test.request.please_add_environment_to_scenario'), 2000);
|
||||
this.request.useEnvironment = false;
|
||||
}
|
||||
this.$refs["request"].clearValidate();
|
||||
}
|
||||
},
|
||||
|
||||
computed: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.request-method-select {
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
.el-tag {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.environment-display {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.environment-name {
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.adjust-margin-bottom {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.environment-url-tip {
|
||||
color: #F56C6C;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -5,7 +5,8 @@
|
|||
<el-input :disabled="isReadOnly" v-model="request.name" maxlength="300" show-word-limit/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="!request.useEnvironment" :label="$t('api_test.request.url')" prop="url" class="adjust-margin-bottom">
|
||||
<el-form-item v-if="!request.useEnvironment" :label="$t('api_test.request.url')" prop="url"
|
||||
class="adjust-margin-bottom">
|
||||
<el-input :disabled="isReadOnly" v-model="request.url" maxlength="500"
|
||||
:placeholder="$t('api_test.request.url_description')" @change="urlChange" clearable>
|
||||
<template v-slot:prepend>
|
||||
|
@ -60,19 +61,20 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import MsApiKeyValue from "./ApiKeyValue";
|
||||
import MsApiBody from "./ApiBody";
|
||||
import MsApiAssertions from "./assertion/ApiAssertions";
|
||||
import {KeyValue, Request} from "../model/ScenarioModel";
|
||||
import MsApiExtract from "./extract/ApiExtract";
|
||||
import ApiRequestMethodSelect from "./collapse/ApiRequestMethodSelect";
|
||||
import {requestHeaders} from "../../../../../common/js/constants";
|
||||
import MsApiKeyValue from "../ApiKeyValue";
|
||||
import MsApiBody from "../ApiBody";
|
||||
import MsApiAssertions from "../assertion/ApiAssertions";
|
||||
import {KeyValue} from "../../model/ScenarioModel";
|
||||
import MsApiExtract from "../extract/ApiExtract";
|
||||
import ApiRequestMethodSelect from "../collapse/ApiRequestMethodSelect";
|
||||
import {REQUEST_HEADERS} from "@/common/js/constants";
|
||||
import {HttpRequest} from "../../model/ScenarioModel";
|
||||
|
||||
export default {
|
||||
name: "MsApiRequestForm",
|
||||
name: "MsApiHttpRequestForm",
|
||||
components: {ApiRequestMethodSelect, MsApiExtract, MsApiAssertions, MsApiBody, MsApiKeyValue},
|
||||
props: {
|
||||
request: Request,
|
||||
request: HttpRequest,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
|
@ -101,7 +103,7 @@
|
|||
{max: 500, required: true, message: this.$t('commons.input_limit', [1, 500]), trigger: 'blur'},
|
||||
]
|
||||
},
|
||||
headerSuggestions: requestHeaders
|
||||
headerSuggestions: REQUEST_HEADERS
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -119,7 +121,8 @@
|
|||
this.request.path = '/' + this.request.path;
|
||||
}
|
||||
let url = this.getURL(this.displayUrl);
|
||||
this.request.path = decodeURIComponent(url.pathname);
|
||||
this
|
||||
.request.path = decodeURIComponent(url.pathname);
|
||||
this.request.urlWirhEnv = decodeURIComponent(url.origin + url.pathname);
|
||||
},
|
||||
getURL(urlStr) {
|
||||
|
@ -169,10 +172,6 @@
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
.request-method-select {
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
.el-tag {
|
||||
width: 100%;
|
||||
height: 40px;
|
|
@ -3,9 +3,12 @@
|
|||
<draggable :list="this.scenario.requests" group="Request" class="request-draggable" ghost-class="request-ghost">
|
||||
<div class="request-item" v-for="(request, index) in this.scenario.requests" :key="index" @click="select(request)"
|
||||
:class="{'selected': isSelected(request)}">
|
||||
<el-row type="flex">
|
||||
<el-row type="flex" align="middle">
|
||||
<div class="request-type">
|
||||
{{request.showType()}}
|
||||
</div>
|
||||
<div class="request-method">
|
||||
{{request.method}}
|
||||
{{request.showMethod()}}
|
||||
</div>
|
||||
<div class="request-name">
|
||||
{{request.name}}
|
||||
|
@ -26,12 +29,20 @@
|
|||
</el-row>
|
||||
</div>
|
||||
</draggable>
|
||||
<el-button :disabled="isReadOnly" class="request-create" type="primary" size="mini" icon="el-icon-plus" plain @click="createRequest"/>
|
||||
<el-popover placement="top" v-model="visible">
|
||||
<el-radio-group v-model="type" @change="createRequest">
|
||||
<el-radio :label="types.HTTP">HTTP</el-radio>
|
||||
<el-radio :label="types.DUBBO">DUBBO</el-radio>
|
||||
</el-radio-group>
|
||||
<el-button slot="reference" :disabled="isReadOnly"
|
||||
class="request-create" type="primary" size="mini" icon="el-icon-plus" plain/>
|
||||
</el-popover>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Request} from "../model/ScenarioModel";
|
||||
import {RequestFactory} from "../../model/ScenarioModel";
|
||||
import draggable from 'vuedraggable';
|
||||
|
||||
export default {
|
||||
|
@ -41,7 +52,6 @@
|
|||
|
||||
props: {
|
||||
scenario: Object,
|
||||
open: Function,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
|
@ -51,6 +61,9 @@
|
|||
data() {
|
||||
return {
|
||||
selected: 0,
|
||||
visible: false,
|
||||
types: RequestFactory.TYPES,
|
||||
type: ""
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -63,13 +76,15 @@
|
|||
},
|
||||
|
||||
methods: {
|
||||
createRequest: function () {
|
||||
let request = new Request();
|
||||
createRequest: function (type) {
|
||||
let request = new RequestFactory({type: type});
|
||||
this.scenario.requests.push(request);
|
||||
this.type = "";
|
||||
this.visible = false;
|
||||
},
|
||||
copyRequest: function (index) {
|
||||
let request = this.scenario.requests[index];
|
||||
this.scenario.requests.push(new Request(request));
|
||||
this.scenario.requests.push(new RequestFactory(request));
|
||||
},
|
||||
deleteRequest: function (index) {
|
||||
this.scenario.requests.splice(index, 1);
|
||||
|
@ -93,7 +108,7 @@
|
|||
request.useEnvironment = false;
|
||||
}
|
||||
this.selected = request;
|
||||
this.open(request);
|
||||
this.$emit("select", request);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -106,7 +121,6 @@
|
|||
<style scoped>
|
||||
.request-item {
|
||||
border-left: 5px solid #1E90FF;
|
||||
line-height: 40px;
|
||||
max-height: 40px;
|
||||
border-top: 1px solid #EBEEF5;
|
||||
cursor: pointer;
|
||||
|
@ -124,6 +138,19 @@
|
|||
background-color: #F5F5F5;
|
||||
}
|
||||
|
||||
.request-type {
|
||||
background-color: #409eff;
|
||||
color: #fff;
|
||||
margin-left: 5px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 20px;
|
||||
white-space: nowrap;
|
||||
font-size: 12px;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.request-method {
|
||||
padding: 0 5px;
|
||||
color: #1E90FF;
|
|
@ -0,0 +1,38 @@
|
|||
<template>
|
||||
<component :is="component" :is-read-only="isReadOnly" :request="request"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Request, RequestFactory} from "../../model/ScenarioModel";
|
||||
import MsApiHttpRequestForm from "./ApiHttpRequestForm";
|
||||
import MsApiDubboRequestForm from "./ApiDubboRequestForm";
|
||||
|
||||
export default {
|
||||
name: "MsApiRequestForm",
|
||||
components: {MsApiDubboRequestForm, MsApiHttpRequestForm},
|
||||
props: {
|
||||
request: Request,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
component({request: {type}}) {
|
||||
let name;
|
||||
switch (type) {
|
||||
case RequestFactory.TYPES.DUBBO:
|
||||
name = "MsApiDubboRequestForm";
|
||||
break;
|
||||
default:
|
||||
name = "MsApiHttpRequestForm";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,72 @@
|
|||
<template>
|
||||
<el-form :model="config" :rules="rules" ref="config" label-width="100px" size="small">
|
||||
<el-form-item label="Protocol" prop="protocol" class="dubbo-form-item">
|
||||
<el-select v-model="config.protocol" class="select-100">
|
||||
<el-option v-for="p in protocols" :key="p" :label="p" :value="p"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Group" prop="group" class="dubbo-form-item">
|
||||
<el-input v-model="config.group" maxlength="300" show-word-limit
|
||||
:placeholder="$t('commons.input_content')"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Namespace" prop="namespace" class="dubbo-form-item">
|
||||
<el-input v-model="config.namespace" maxlength="300" show-word-limit
|
||||
:placeholder="$t('commons.input_content')"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="UserName" prop="username" class="dubbo-form-item">
|
||||
<el-input v-model="config.username" maxlength="100" show-word-limit
|
||||
:placeholder="$t('commons.input_content')"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Address" prop="address" class="dubbo-form-item-long">
|
||||
<el-input v-model="config.address" maxlength="300" show-word-limit
|
||||
:placeholder="$t('commons.input_content')"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="Password" prop="password" class="dubbo-form-item">
|
||||
<el-input v-model="config.password" maxlength="30" show-word-limit show-password autocomplete="new-password"
|
||||
:placeholder="$t('commons.input_content')"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Timeout" prop="timeout" class="dubbo-form-item">
|
||||
<el-input type="number" v-model="config.timeout" :placeholder="$t('commons.input_content')"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import './dubbo.css'
|
||||
import {ConfigCenter} from "@/business/components/api/test/model/ScenarioModel";
|
||||
|
||||
export default {
|
||||
name: "MsDubboConfigCenter",
|
||||
props: {
|
||||
config: ConfigCenter
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
protocols: ConfigCenter.PROTOCOLS,
|
||||
methods: [],
|
||||
rules: {
|
||||
group: [
|
||||
{max: 300, message: this.$t('commons.input_limit', [0, 300]), trigger: 'blur'}
|
||||
],
|
||||
namespace: [
|
||||
{max: 300, message: this.$t('commons.input_limit', [0, 300]), trigger: 'blur'}
|
||||
],
|
||||
username: [
|
||||
{max: 100, message: this.$t('commons.input_limit', [0, 300]), trigger: 'blur'}
|
||||
],
|
||||
password: [
|
||||
{max: 30, message: this.$t('commons.input_limit', [0, 300]), trigger: 'blur'}
|
||||
],
|
||||
address: [
|
||||
{max: 300, message: this.$t('commons.input_limit', [0, 300]), trigger: 'blur'}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,74 @@
|
|||
<template>
|
||||
|
||||
<el-form :model="consumer" :rules="rules" ref="consumer" label-width="100px" size="small">
|
||||
<el-form-item label="Timeout" prop="timeout" class="dubbo-form-item">
|
||||
<el-input type="number" v-model="consumer.timeout" :placeholder="$t('commons.input_content')"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Version" prop="version" class="dubbo-form-item">
|
||||
<el-input v-model="consumer.version" maxlength="30" show-word-limit
|
||||
:placeholder="$t('commons.input_content')"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Retries" prop="retries" class="dubbo-form-item">
|
||||
<el-input type="number" v-model="consumer.retries" :placeholder="$t('commons.input_content')"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Cluster" prop="cluster" class="dubbo-form-item">
|
||||
<el-input v-model="consumer.cluster" maxlength="300" show-word-limit
|
||||
:placeholder="$t('commons.input_content')"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Group" prop="group" class="dubbo-form-item">
|
||||
<el-input v-model="consumer.group" maxlength="300" show-word-limit
|
||||
:placeholder="$t('commons.input_content')"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Connections" prop="connections" class="dubbo-form-item">
|
||||
<el-input type="number" v-model="consumer.connections" :placeholder="$t('commons.input_content')"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Async" prop="async" class="dubbo-form-item">
|
||||
<el-select v-model="consumer.async" class="select-100">
|
||||
<el-option v-for="option in asyncOptions" :key="option" :label="option" :value="option"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="LoadBalance" prop="loadBalance" class="dubbo-form-item">
|
||||
<el-select v-model="consumer.loadBalance" class="select-100">
|
||||
<el-option v-for="option in loadBalances" :key="option" :label="option" :value="option"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import './dubbo.css'
|
||||
import {ConsumerAndService} from "@/business/components/api/test/model/ScenarioModel";
|
||||
|
||||
export default {
|
||||
name: "MsDubboConsumerService",
|
||||
props: {
|
||||
consumer: ConsumerAndService
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
asyncOptions: ConsumerAndService.ASYNC_OPTIONS,
|
||||
loadBalances: ConsumerAndService.LOAD_BALANCE_OPTIONS,
|
||||
methods: [],
|
||||
rules: {
|
||||
version: [
|
||||
{max: 30, message: this.$t('commons.input_limit', [0, 30]), trigger: 'blur'}
|
||||
],
|
||||
cluster: [
|
||||
{max: 300, message: this.$t('commons.input_limit', [0, 300]), trigger: 'blur'}
|
||||
],
|
||||
group: [
|
||||
{max: 300, message: this.$t('commons.input_limit', [0, 300]), trigger: 'blur'}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,77 @@
|
|||
<template>
|
||||
<el-form :model="request" :rules="rules" ref="request" label-width="100px" size="small">
|
||||
<el-button class="get-provider" type="primary" size="small" @click="getProviderList">Get Provider List</el-button>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="Interfaces" prop="interfaces">
|
||||
<el-select v-model="request.interface" class="select-100">
|
||||
<el-option v-for="i in interfaces" :key="i.value" :label="i.label" :value="i.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="Methods" prop="methods">
|
||||
<el-select v-model="request.method" class="select-100">
|
||||
<el-option v-for="i in methods" :key="i.value" :label="i.label" :value="i.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="Interface" prop="interface">
|
||||
<el-input v-model="request.interface" maxlength="300" show-word-limit
|
||||
:placeholder="$t('commons.input_content')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="Method" prop="method">
|
||||
<el-input v-model="request.method" maxlength="300" show-word-limit
|
||||
:placeholder="$t('commons.input_content')"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {DubboRequest} from "@/business/components/api/test/model/ScenarioModel";
|
||||
|
||||
export default {
|
||||
name: "MsDubboInterface",
|
||||
props: {
|
||||
request: DubboRequest
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
interfaces: [],
|
||||
methods: [],
|
||||
rules: {
|
||||
interface: [
|
||||
{max: 300, message: this.$t('commons.input_limit', [1, 300]), trigger: 'blur'}
|
||||
],
|
||||
method: [
|
||||
{max: 300, message: this.$t('commons.input_limit', [1, 300]), trigger: 'blur'}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
getProviderList() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.get-provider {
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
|
||||
.select-100 {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,65 @@
|
|||
<template>
|
||||
<el-form :model="registry" :rules="rules" ref="registry" label-width="100px" size="small">
|
||||
<el-form-item label="Protocol" prop="protocol" class="dubbo-form-item">
|
||||
<el-select v-model="registry.protocol" class="select-100">
|
||||
<el-option v-for="p in protocols" :key="p" :label="p" :value="p"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Group" prop="group" class="dubbo-form-item">
|
||||
<el-input v-model="registry.group" maxlength="300" show-word-limit
|
||||
:placeholder="$t('commons.input_content')"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="UserName" prop="username" class="dubbo-form-item">
|
||||
<el-input v-model="registry.username" maxlength="100" show-word-limit
|
||||
:placeholder="$t('commons.input_content')"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Password" prop="password" class="dubbo-form-item">
|
||||
<el-input v-model="registry.password" maxlength="30" show-word-limit show-password autocomplete="new-password"
|
||||
:placeholder="$t('commons.input_content')"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Address" prop="address" class="dubbo-form-item-long">
|
||||
<el-input v-model="registry.address" maxlength="300" show-word-limit
|
||||
:placeholder="$t('commons.input_content')"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Timeout" prop="timeout" class="dubbo-form-item">
|
||||
<el-input type="number" v-model="registry.timeout" :placeholder="$t('commons.input_content')"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import './dubbo.css'
|
||||
import {RegistryCenter} from "@/business/components/api/test/model/ScenarioModel";
|
||||
|
||||
export default {
|
||||
name: "MsDubboRegistryCenter",
|
||||
props: {
|
||||
registry: RegistryCenter
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
protocols: RegistryCenter.PROTOCOLS,
|
||||
methods: [],
|
||||
rules: {
|
||||
group: [
|
||||
{max: 300, message: this.$t('commons.input_limit', [0, 300]), trigger: 'blur'}
|
||||
],
|
||||
username: [
|
||||
{max: 300, message: this.$t('commons.input_limit', [0, 300]), trigger: 'blur'}
|
||||
],
|
||||
password: [
|
||||
{max: 30, message: this.$t('commons.input_limit', [0, 30]), trigger: 'blur'}
|
||||
],
|
||||
address: [
|
||||
{max: 300, message: this.$t('commons.input_limit', [0, 300]), trigger: 'blur'}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,37 @@
|
|||
@media only screen and (max-width: 1200px) {
|
||||
.dubbo-form-item {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.dubbo-form-item-long {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1201px) and (max-width: 1600px) {
|
||||
.dubbo-form-item {
|
||||
width: 33.33%;
|
||||
}
|
||||
|
||||
.dubbo-form-item-long {
|
||||
width: 66.67%;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1601px) {
|
||||
.dubbo-form-item {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.dubbo-form-item-long {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.dubbo-form-item, .dubbo-form-item-long {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.select-100 {
|
||||
width: 100%;
|
||||
}
|
|
@ -220,6 +220,60 @@ export class ThreadGroup extends DefaultTestElement {
|
|||
}
|
||||
}
|
||||
|
||||
export class DubboSample extends DefaultTestElement {
|
||||
constructor(testName, request = {}) {
|
||||
super('io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample',
|
||||
'io.github.ningyu.jmeter.plugin.dubbo.gui.DubboSampleGui',
|
||||
'io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample', testName);
|
||||
this.request = request;
|
||||
|
||||
this.stringProp("FIELD_DUBBO_CONFIG_CENTER_PROTOCOL", this.request.configCenter.protocol);
|
||||
this.stringProp("FIELD_DUBBO_CONFIG_CENTER_GROUP", this.request.configCenter.group);
|
||||
this.stringProp("FIELD_DUBBO_CONFIG_CENTER_NAMESPACE", this.request.configCenter.namespace);
|
||||
this.stringProp("FIELD_DUBBO_CONFIG_CENTER_USER_NAME", this.request.configCenter.username);
|
||||
this.stringProp("FIELD_DUBBO_CONFIG_CENTER_PASSWORD", this.request.configCenter.password);
|
||||
this.stringProp("FIELD_DUBBO_CONFIG_CENTER_ADDRESS", this.request.configCenter.address);
|
||||
this.stringProp("FIELD_DUBBO_CONFIG_CENTER_TIMEOUT", this.request.configCenter.timeout);
|
||||
|
||||
this.stringProp("FIELD_DUBBO_REGISTRY_PROTOCOL", this.request.registryCenter.protocol);
|
||||
this.stringProp("FIELD_DUBBO_REGISTRY_GROUP", this.request.registryCenter.group);
|
||||
this.stringProp("FIELD_DUBBO_REGISTRY_USER_NAME", this.request.registryCenter.username);
|
||||
this.stringProp("FIELD_DUBBO_REGISTRY_PASSWORD", this.request.registryCenter.password);
|
||||
this.stringProp("FIELD_DUBBO_ADDRESS", this.request.registryCenter.address);
|
||||
this.stringProp("FIELD_DUBBO_REGISTRY_TIMEOUT", this.request.registryCenter.timeout);
|
||||
|
||||
this.stringProp("FIELD_DUBBO_TIMEOUT", this.request.consumerAndService.timeout);
|
||||
this.stringProp("FIELD_DUBBO_VERSION", this.request.consumerAndService.version);
|
||||
this.stringProp("FIELD_DUBBO_RETRIES", this.request.consumerAndService.retries);
|
||||
this.stringProp("FIELD_DUBBO_GROUP", this.request.consumerAndService.group);
|
||||
this.stringProp("FIELD_DUBBO_CONNECTIONS", this.request.consumerAndService.connections);
|
||||
this.stringProp("FIELD_DUBBO_LOADBALANCE", this.request.consumerAndService.loadBalance);
|
||||
this.stringProp("FIELD_DUBBO_ASYNC", this.request.consumerAndService.async);
|
||||
this.stringProp("FIELD_DUBBO_CLUSTER", this.request.consumerAndService.cluster);
|
||||
|
||||
this.stringProp("FIELD_DUBBO_RPC_PROTOCOL", this.request.protocol);
|
||||
this.stringProp("FIELD_DUBBO_INTERFACE", this.request.interface);
|
||||
this.stringProp("FIELD_DUBBO_METHOD", this.request.method);
|
||||
|
||||
this.intProp("FIELD_DUBBO_METHOD_ARGS_SIZE", this.request.args.length);
|
||||
this.intProp("FIELD_DUBBO_ATTACHMENT_ARGS_SIZE", this.request.attachmentArgs.length);
|
||||
this.request.args.forEach((arg, i) => {
|
||||
if (!!arg.name || !!arg.value) {
|
||||
let index = i + 1;
|
||||
this.stringProp("FIELD_DUBBO_METHOD_ARGS_PARAM_TYPE" + index, arg.name);
|
||||
this.stringProp("FIELD_DUBBO_METHOD_ARGS_PARAM_VALUE" + index, arg.value);
|
||||
}
|
||||
})
|
||||
this.request.attachmentArgs.forEach((arg, i) => {
|
||||
if (!!arg.name || !!arg.value) {
|
||||
let index = i + 1;
|
||||
this.stringProp("FIELD_DUBBO_ATTACHMENT_ARGS_KEY" + index, arg.name);
|
||||
this.stringProp("FIELD_DUBBO_ATTACHMENT_ARGS_VALUE" + index, arg.value);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class HTTPSamplerProxy extends DefaultTestElement {
|
||||
constructor(testName, request) {
|
||||
super('HTTPSamplerProxy', 'HttpTestSampleGui', 'HTTPSamplerProxy', testName);
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
ResponseCodeAssertion,
|
||||
ResponseDataAssertion,
|
||||
ResponseHeadersAssertion,
|
||||
RegexExtractor, JSONPostProcessor, XPath2Extractor,
|
||||
RegexExtractor, JSONPostProcessor, XPath2Extractor, DubboSample,
|
||||
} from "./JMX";
|
||||
|
||||
export const uuid = function () {
|
||||
|
@ -74,7 +74,7 @@ export class BaseConfig {
|
|||
if (types) {
|
||||
for (let name in types) {
|
||||
if (types.hasOwnProperty(name) && options.hasOwnProperty(name)) {
|
||||
options[name].forEach((o) => {
|
||||
options[name].forEach(o => {
|
||||
this[name].push(new types[name](o));
|
||||
})
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ export class Test extends BaseConfig {
|
|||
constructor(options) {
|
||||
super();
|
||||
this.type = "MS API CONFIG";
|
||||
this.version = '1.0.0';
|
||||
this.version = '1.1.0';
|
||||
this.id = uuid();
|
||||
this.name = undefined;
|
||||
this.projectId = undefined;
|
||||
|
@ -140,7 +140,7 @@ export class Test extends BaseConfig {
|
|||
}
|
||||
|
||||
export class Scenario extends BaseConfig {
|
||||
constructor(options) {
|
||||
constructor(options = {}) {
|
||||
super();
|
||||
this.name = undefined;
|
||||
this.url = undefined;
|
||||
|
@ -148,14 +148,20 @@ export class Scenario extends BaseConfig {
|
|||
this.headers = [];
|
||||
this.requests = [];
|
||||
this.environmentId = undefined;
|
||||
this.dubboConfig = undefined;
|
||||
|
||||
this.set(options);
|
||||
this.sets({variables: KeyValue, headers: KeyValue, requests: Request}, options);
|
||||
this.sets({variables: KeyValue, headers: KeyValue, requests: RequestFactory}, options);
|
||||
}
|
||||
|
||||
initOptions(options) {
|
||||
options = options || {};
|
||||
options.requests = options.requests || [new Request()];
|
||||
options.requests = options.requests || [new RequestFactory()];
|
||||
options.dubboConfig = {
|
||||
configCenter: new ConfigCenter(options.configCenter),
|
||||
registryCenter: new RegistryCenter(options.configCenter),
|
||||
consumerAndService: new ConsumerAndService(options.configCenter),
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
|
@ -173,9 +179,41 @@ export class Scenario extends BaseConfig {
|
|||
}
|
||||
}
|
||||
|
||||
export class RequestFactory {
|
||||
static TYPES = {
|
||||
HTTP: "HTTP",
|
||||
DUBBO: "DUBBO",
|
||||
}
|
||||
|
||||
constructor(options = {}) {
|
||||
options.type = options.type || RequestFactory.TYPES.HTTP
|
||||
switch (options.type) {
|
||||
case RequestFactory.TYPES.DUBBO:
|
||||
return new DubboRequest(options);
|
||||
default:
|
||||
return new HttpRequest(options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Request extends BaseConfig {
|
||||
constructor(options) {
|
||||
constructor(type) {
|
||||
super();
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
showType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
showMethod() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export class HttpRequest extends Request {
|
||||
constructor(options) {
|
||||
super(RequestFactory.TYPES.HTTP);
|
||||
this.name = undefined;
|
||||
this.url = undefined;
|
||||
this.path = undefined;
|
||||
|
@ -204,6 +242,119 @@ export class Request extends BaseConfig {
|
|||
isValid() {
|
||||
return ((!this.useEnvironment && !!this.url) || (this.useEnvironment && !!this.path && this.environment)) && !!this.method
|
||||
}
|
||||
|
||||
showType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
showMethod() {
|
||||
return this.method.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
export class DubboRequest extends Request {
|
||||
static PROTOCOLS = {
|
||||
DUBBO: "dubbo://",
|
||||
RMI: "rmi://",
|
||||
}
|
||||
|
||||
constructor(options = {}) {
|
||||
super(RequestFactory.TYPES.DUBBO);
|
||||
this.name = options.name;
|
||||
this.protocol = options.protocol || DubboRequest.PROTOCOLS.DUBBO;
|
||||
this.interface = options.interface;
|
||||
this.method = options.method;
|
||||
this.configCenter = new ConfigCenter(options.configCenter);
|
||||
this.registryCenter = new RegistryCenter(options.registryCenter);
|
||||
this.consumerAndService = new ConsumerAndService(options.consumerAndService);
|
||||
this.args = [];
|
||||
this.attachmentArgs = [];
|
||||
this.assertions = new Assertions(options.assertions);
|
||||
this.extract = new Extract(options.extract);
|
||||
|
||||
this.sets({args: KeyValue, attachmentArgs: KeyValue}, options);
|
||||
}
|
||||
|
||||
isValid() {
|
||||
return !!this.name;
|
||||
}
|
||||
|
||||
showType() {
|
||||
return "RPC";
|
||||
}
|
||||
|
||||
showMethod() {
|
||||
// dubbo:// -> DUBBO
|
||||
return this.protocol.substr(0, this.protocol.length - 3).toUpperCase();
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new DubboRequest(this);
|
||||
}
|
||||
}
|
||||
|
||||
export class ConfigCenter extends BaseConfig {
|
||||
static PROTOCOLS = ["zookeeper", "nacos", "apollo"];
|
||||
|
||||
constructor(options) {
|
||||
super();
|
||||
this.protocol = undefined;
|
||||
this.group = undefined;
|
||||
this.namespace = undefined;
|
||||
this.username = undefined;
|
||||
this.address = undefined;
|
||||
this.password = undefined;
|
||||
this.timeout = undefined;
|
||||
|
||||
this.set(options);
|
||||
}
|
||||
|
||||
isValid() {
|
||||
return !!this.protocol || !!this.group || !!this.namespace || !!this.username || !!this.address || !!this.password || !!this.timeout;
|
||||
}
|
||||
}
|
||||
|
||||
export class RegistryCenter extends BaseConfig {
|
||||
static PROTOCOLS = ["none", "zookeeper", "nacos", "apollo", "multicast", "redis", "simple"];
|
||||
|
||||
constructor(options) {
|
||||
super();
|
||||
this.protocol = undefined;
|
||||
this.group = undefined;
|
||||
this.username = undefined;
|
||||
this.address = undefined;
|
||||
this.password = undefined;
|
||||
this.timeout = undefined;
|
||||
|
||||
this.set(options);
|
||||
}
|
||||
|
||||
isValid() {
|
||||
return !!this.protocol || !!this.group || !!this.username || !!this.address || !!this.password || !!this.timeout;
|
||||
}
|
||||
}
|
||||
|
||||
export class ConsumerAndService extends BaseConfig {
|
||||
static ASYNC_OPTIONS = ["sync", "async"];
|
||||
static LOAD_BALANCE_OPTIONS = ["random", "roundrobin", "leastactive", "consistenthash"];
|
||||
|
||||
constructor(options) {
|
||||
super();
|
||||
this.timeout = "1000";
|
||||
this.version = "1.0";
|
||||
this.retries = "0";
|
||||
this.cluster = "failfast";
|
||||
this.group = undefined;
|
||||
this.connections = "100";
|
||||
this.async = "sync";
|
||||
this.loadBalance = "random";
|
||||
|
||||
this.set(options);
|
||||
}
|
||||
|
||||
isValid() {
|
||||
return !!this.timeout || !!this.version || !!this.retries || !!this.cluster || !!this.group || !!this.connections || !!this.async || !!this.loadBalance;
|
||||
}
|
||||
}
|
||||
|
||||
export class Body extends BaseConfig {
|
||||
|
@ -389,9 +540,9 @@ const JMX_ASSERTION_CONDITION = {
|
|||
OR: 1 << 5
|
||||
}
|
||||
|
||||
class JMXRequest {
|
||||
class JMXHttpRequest {
|
||||
constructor(request) {
|
||||
if (request && request instanceof Request && (request.url || request.path)) {
|
||||
if (request && request instanceof HttpRequest && (request.url || request.path)) {
|
||||
this.useEnvironment = request.useEnvironment;
|
||||
this.method = request.method;
|
||||
if (!request.useEnvironment) {
|
||||
|
@ -421,7 +572,38 @@ class JMXRequest {
|
|||
}
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
class JMXDubboRequest {
|
||||
constructor(request, dubboConfig) {
|
||||
// Request 复制
|
||||
let obj = request.clone();
|
||||
// 去掉无效的kv
|
||||
obj.args = obj.args.filter(arg => {
|
||||
return arg.isValid();
|
||||
});
|
||||
obj.attachmentArgs = obj.attachmentArgs.filter(arg => {
|
||||
return arg.isValid();
|
||||
});
|
||||
|
||||
// Scenario DubboConfig复制,需要Request中的内容无效才会复制
|
||||
this.copy(obj.configCenter, dubboConfig.configCenter);
|
||||
this.copy(obj.registryCenter, dubboConfig.registryCenter);
|
||||
this.copy(obj.consumerAndService, dubboConfig.consumerAndService);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
copy(source, target) {
|
||||
if (source.isValid()) return;
|
||||
|
||||
let keys = Object.keys(target);
|
||||
keys.forEach(key => {
|
||||
if (source[key] === undefined) {
|
||||
source[key] = target[key];
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class JMeterTestPlan extends Element {
|
||||
|
@ -463,22 +645,27 @@ class JMXGenerator {
|
|||
|
||||
scenario.requests.forEach(request => {
|
||||
if (!request.isValid()) return;
|
||||
let sampler;
|
||||
|
||||
let httpSamplerProxy = new HTTPSamplerProxy(request.name || "", new JMXRequest(request));
|
||||
|
||||
this.addRequestHeader(httpSamplerProxy, request);
|
||||
|
||||
if (request.method.toUpperCase() === 'GET') {
|
||||
this.addRequestArguments(httpSamplerProxy, request);
|
||||
} else {
|
||||
this.addRequestBody(httpSamplerProxy, request);
|
||||
if (request instanceof DubboRequest) {
|
||||
sampler = new DubboSample(request.name || "", new JMXDubboRequest(request, scenario.dubboConfig));
|
||||
}
|
||||
|
||||
this.addRequestAssertion(httpSamplerProxy, request);
|
||||
if (request instanceof HttpRequest) {
|
||||
let sampler = new HTTPSamplerProxy(request.name || "", new JMXHttpRequest(request));
|
||||
this.addRequestHeader(sampler, request);
|
||||
if (request.method.toUpperCase() === 'GET') {
|
||||
this.addRequestArguments(sampler, request);
|
||||
} else {
|
||||
this.addRequestBody(sampler, request);
|
||||
}
|
||||
}
|
||||
|
||||
this.addRequestExtractor(httpSamplerProxy, request);
|
||||
this.addRequestAssertion(sampler, request);
|
||||
|
||||
threadGroup.put(httpSamplerProxy);
|
||||
this.addRequestExtractor(sampler, request);
|
||||
|
||||
threadGroup.put(sampler);
|
||||
})
|
||||
|
||||
testPlan.put(threadGroup);
|
||||
|
|
|
@ -19,7 +19,7 @@ export const ZH_CN = 'zh_CN';
|
|||
export const ZH_TW = 'zh_TW';
|
||||
export const EN_US = 'en_US';
|
||||
|
||||
export const requestHeaders = [
|
||||
export const REQUEST_HEADERS = [
|
||||
{value: 'Accept'},
|
||||
{value: 'Accept-Charset'},
|
||||
{value: 'Accept-Language'},
|
||||
|
|
|
@ -344,6 +344,7 @@ export default {
|
|||
please_save_test: "Please Save Test First",
|
||||
},
|
||||
scenario: {
|
||||
dubbo: "Dubbo Config",
|
||||
config: "Scenario Config",
|
||||
input_name: "Please enter the scenario name",
|
||||
name: "Scenario Name",
|
||||
|
@ -401,6 +402,9 @@ export default {
|
|||
regex_expression: "Regular expression",
|
||||
json_path_expression: "JSONPath expression",
|
||||
xpath_expression: "XPath expression",
|
||||
},
|
||||
dubbo: {
|
||||
protocol: "protocol"
|
||||
}
|
||||
},
|
||||
api_import: {
|
||||
|
|
|
@ -343,6 +343,7 @@ export default {
|
|||
please_save_test: "请先保存测试",
|
||||
},
|
||||
scenario: {
|
||||
dubbo: "Dubbo配置",
|
||||
config: "场景配置",
|
||||
input_name: "请输入场景名称",
|
||||
name: "场景名称",
|
||||
|
@ -400,6 +401,9 @@ export default {
|
|||
regex_expression: "Perl型正则表达式",
|
||||
json_path_expression: "JSONPath表达式",
|
||||
xpath_expression: "XPath表达式",
|
||||
},
|
||||
dubbo: {
|
||||
protocol: "协议"
|
||||
}
|
||||
},
|
||||
api_import: {
|
||||
|
|
|
@ -342,6 +342,7 @@ export default {
|
|||
please_save_test: "請先保存測試",
|
||||
},
|
||||
scenario: {
|
||||
dubbo: "Dubbo配寘",
|
||||
creator: "創建人",
|
||||
config: "場景配寘",
|
||||
input_name: "請輸入場景名稱",
|
||||
|
@ -400,6 +401,9 @@ export default {
|
|||
regex_expression: "Perl型規則運算式",
|
||||
json_path_expression: "JSONPath運算式",
|
||||
xpath_expression: "XPath運算式",
|
||||
},
|
||||
dubbo: {
|
||||
protocol: "協定"
|
||||
}
|
||||
},
|
||||
api_import: {
|
||||
|
|
Loading…
Reference in New Issue