diff --git a/README.md b/README.md index d1f25726d0..3992f40dee 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,21 @@ curl -sSL https://github.com/metersphere/metersphere/releases/latest/download/qu - [完整文档](https://metersphere.io/docs/) - [演示视频](http://video.fit2cloud.com/%E3%80%90%E6%BC%94%E7%A4%BA%E8%A7%86%E9%A2%91%E3%80%91202006%20MeterSphere%20v1.0%20%E5%8A%9F%E8%83%BD%E6%BC%94%E7%A4%BA.mp4) +## 版本说明 + +MeterSphere 版本号命名规则为:v大版本.功能版本.Bug修复版本。比如: + +``` +v1.0.1 是 v1.0.0 之后的Bug修复版本; +v1.1.0 是 v1.0.0 之后的功能版本。 +``` +像其它优秀开源项目一样,MeterSphere 将每月发布一个功能版本,并同时维护 3 个功能版本。比如: + +``` +在 v1.3 发布前,我们会同时维护 v1.0、v1.1、v1.2; +在 v1.3 发布后,我们会同时维护 v1.1、v1.2、v1.3;v1.0 会停止维护。 +``` + ## 技术优势 - 全生命周期: 能够覆盖从测试计划到测试执行、测试报告分析的不同阶段; diff --git a/backend/pom.xml b/backend/pom.xml index 742a2efd97..8acf99d312 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -154,6 +154,26 @@ + + org.apache.dubbo + dubbo + 2.7.7 + + + org.apache.zookeeper + zookeeper + 3.4.13 + + + org.apache.curator + curator-framework + 4.0.1 + + + org.apache.curator + curator-recipes + 4.0.1 + com.alibaba diff --git a/backend/src/main/java/io/metersphere/api/dubbo/MethodArgument.java b/backend/src/main/java/io/metersphere/api/dubbo/MethodArgument.java new file mode 100644 index 0000000000..a9efb359b8 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dubbo/MethodArgument.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.metersphere.api.dubbo; + + +import io.metersphere.api.dubbo.utils.JsonUtils; +import io.metersphere.api.dubbo.utils.StringUtils; + +import java.io.Serializable; + + +/** + * MethodArgument + */ +public class MethodArgument implements Serializable { + + private static final long serialVersionUID = -2567457932227227262L; + private String paramType; + private String paramValue; + + public MethodArgument(String paramType, String paramValue) { + setParamType(paramType); + setParamValue(paramValue); + } + + public String getParamType() { + return paramType; + } + + public void setParamType(String paramType) { + this.paramType = (paramType == null ? null : StringUtils.trimAllWhitespace(paramType)); + } + + public String getParamValue() { + return paramValue; + } + + public void setParamValue(String paramValue) { + this.paramValue = (paramValue == null ? null : StringUtils.trimWhitespace(paramValue)); + } + + @Override + public String toString() { + return JsonUtils.toJson(this); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((paramType == null) ? 0 : paramType.hashCode()); + result = prime * result + ((paramValue == null) ? 0 : paramValue.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + MethodArgument other = (MethodArgument) obj; + if (paramType == null) { + if (other.paramType != null) + return false; + } else if (!paramType.equals(other.paramType)) + return false; + if (paramValue == null) { + if (other.paramValue != null) + return false; + } else if (!paramValue.equals(other.paramValue)) + return false; + return true; + } + +} diff --git a/backend/src/main/java/io/metersphere/api/dubbo/ProviderService.java b/backend/src/main/java/io/metersphere/api/dubbo/ProviderService.java new file mode 100644 index 0000000000..3478a864ac --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dubbo/ProviderService.java @@ -0,0 +1,112 @@ +package io.metersphere.api.dubbo; + +import io.metersphere.api.dubbo.utils.Constants; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.ReferenceConfig; +import org.apache.dubbo.config.ReferenceConfigBase; +import org.apache.dubbo.config.RegistryConfig; +import org.apache.dubbo.config.utils.ReferenceConfigCache; +import org.apache.dubbo.registry.RegistryService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * ProviderService + */ +public class ProviderService implements Serializable { + + private static final Logger log = LoggerFactory.getLogger(ProviderService.class); + + private static final long serialVersionUID = -750353929981409079L; + ConcurrentMap> providerUrls = null; + + private static ConcurrentMap cache = new ConcurrentHashMap<>(); + + public static ProviderService get(String key) { + ProviderService service = cache.get(key); + if (service == null) { + cache.putIfAbsent(key, new ProviderService()); + service = cache.get(key); + } + return service; + } + + public Map findByService(String serviceName) { + return providerUrls == null ? null : providerUrls.get(serviceName); + } + + public List getProviders(String protocol, String address, String group) throws RuntimeException { + if (protocol.equals("zookeeper") || protocol.equals("nacos") || protocol.equals("redis")) { + return executeRegistry(protocol, address, group); +// } else if (protocol.equals("none")) { +// return executeTelnet(); + } else { + throw new RuntimeException("Registry Protocol please use zookeeper or nacos or redis!"); + } + } + + private List executeTelnet() throws RuntimeException { + throw new RuntimeException(); + } + + private List executeRegistry(String protocol, String address, String group) throws RuntimeException { + ReferenceConfig reference = new ReferenceConfig(); + // set application + reference.setApplication(new ApplicationConfig("DubboSample")); + RegistryConfig registry = null; + switch (protocol) { + case Constants.REGISTRY_ZOOKEEPER: + registry = new RegistryConfig(); + registry.setProtocol(Constants.REGISTRY_ZOOKEEPER); + registry.setGroup(group); + registry.setAddress(address); + reference.setRegistry(registry); + break; + case Constants.REGISTRY_NACOS: + registry = new RegistryConfig(); + registry.setProtocol(Constants.REGISTRY_NACOS); + registry.setGroup(group); + registry.setAddress(address); + reference.setRegistry(registry); + break; + case Constants.REGISTRY_REDIS: + registry = new RegistryConfig(); + registry.setProtocol(Constants.REGISTRY_REDIS); + registry.setGroup(group); + registry.setAddress(address); + reference.setRegistry(registry); + break; + } + reference.setInterface("org.apache.dubbo.registry.RegistryService"); + try { + ReferenceConfigCache cache = ReferenceConfigCache.getCache(address + "_" + group, new ReferenceConfigCache.KeyGenerator() { + + @Override + public String generateKey(ReferenceConfigBase referenceConfig) { + return referenceConfig.toString(); + } + }); + RegistryService registryService = (RegistryService) cache.get(reference); + if (registryService == null) { + throw new RuntimeException("Can't get the interface list, please check if the address is wrong!"); + } + RegistryServerSync registryServerSync = RegistryServerSync.get(address + "_" + group); + registryService.subscribe(RegistryServerSync.SUBSCRIBE, registryServerSync); + List ret = new ArrayList(); + providerUrls = registryServerSync.getRegistryCache().get(com.alibaba.dubbo.common.Constants.PROVIDERS_CATEGORY); + if (providerUrls != null) ret.addAll(providerUrls.keySet()); + return ret; + } catch (Exception e) { + log.error("get provider list is error!", e); + throw new RuntimeException("Can't get the interface list, please check if the address is wrong!", e); + } + } +} diff --git a/backend/src/main/java/io/metersphere/api/dubbo/RegistryServerSync.java b/backend/src/main/java/io/metersphere/api/dubbo/RegistryServerSync.java new file mode 100644 index 0000000000..6a58c8349d --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dubbo/RegistryServerSync.java @@ -0,0 +1,171 @@ +package io.metersphere.api.dubbo; + +import io.metersphere.api.dubbo.utils.MD5Util; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.constants.CommonConstants; +import org.apache.dubbo.common.constants.RegistryConstants; +import org.apache.dubbo.common.utils.NetUtils; +import org.apache.dubbo.common.utils.StringUtils; +import org.apache.dubbo.registry.Constants; +import org.apache.dubbo.registry.NotifyListener; + +import java.io.Serializable; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * RegistryServerSync + */ +public class RegistryServerSync implements NotifyListener, Serializable { + + private static final long serialVersionUID = -1744756264793278229L; + private static ConcurrentMap cache = new ConcurrentHashMap<>(); + + public static RegistryServerSync get(String key) { + RegistryServerSync sync = cache.get(key); + if (sync == null) { + cache.putIfAbsent(key, new RegistryServerSync()); + sync = cache.get(key); + } + return sync; + } + + public static final URL SUBSCRIBE = new URL(Constants.ADMIN_PROTOCOL, NetUtils.getLocalHost(), 0, "", + CommonConstants.INTERFACE_KEY, CommonConstants.ANY_VALUE, + CommonConstants.GROUP_KEY, CommonConstants.ANY_VALUE, + CommonConstants.VERSION_KEY, CommonConstants.ANY_VALUE, + CommonConstants.CLASSIFIER_KEY, CommonConstants.ANY_VALUE, + RegistryConstants.CATEGORY_KEY, RegistryConstants.PROVIDERS_CATEGORY, +// Constants.CATEGORY_KEY, Constants.PROVIDERS_CATEGORY + "," +// + Constants.CONSUMERS_CATEGORY + "," +// + Constants.ROUTERS_CATEGORY + "," +// + Constants.CONFIGURATORS_CATEGORY, + CommonConstants.ENABLED_KEY, CommonConstants.ANY_VALUE, + CommonConstants.CHECK_KEY, String.valueOf(false)); + + // ConcurrentMap>> + private final ConcurrentMap>> + registryCache = new ConcurrentHashMap<>(); + /** + * Make sure ID never changed when the same url notified many times + */ + private final ConcurrentHashMap URL_IDS_MAPPER = new ConcurrentHashMap<>(); + + public RegistryServerSync() { + } + + public ConcurrentMap>> getRegistryCache() { + return registryCache; + } + + @Override + public void notify(List urls) { + if (urls == null || urls.isEmpty()) { + return; + } + // Map>> + final Map>> categories = new HashMap<>(); + String interfaceName = null; + for (URL url : urls) { + String category = url.getParameter(RegistryConstants.CATEGORY_KEY, RegistryConstants.PROVIDERS_CATEGORY); + if (RegistryConstants.EMPTY_PROTOCOL.equalsIgnoreCase(url.getProtocol())) { // NOTE: group and version in empty protocol is * + ConcurrentMap> services = registryCache.get(category); + if (services != null) { + String group = url.getParameter(CommonConstants.GROUP_KEY); + String version = url.getParameter(CommonConstants.VERSION_KEY); + // NOTE: group and version in empty protocol is * + if (!CommonConstants.ANY_VALUE.equals(group) && !CommonConstants.ANY_VALUE.equals(version)) { + services.remove(url.getServiceKey()); + } else { + for (Map.Entry> serviceEntry : services.entrySet()) { + String service = serviceEntry.getKey(); + if (this.getInterface(service).equals(url.getServiceInterface()) + && (CommonConstants.ANY_VALUE.equals(group) || StringUtils.isEquals(group, this.getGroup(service))) + && (CommonConstants.ANY_VALUE.equals(version) || StringUtils.isEquals(version, this.getVersion(service)))) { + services.remove(service); + } + } + } + } + } else { + if (StringUtils.isEmpty(interfaceName)) { + interfaceName = url.getServiceInterface(); + } + Map> services = categories.get(category); + if (services == null) { + services = new HashMap<>(); + categories.put(category, services); + } + String service = url.getServiceKey(); + Map ids = services.get(service); + if (ids == null) { + ids = new HashMap<>(); + services.put(service, ids); + } + + // Make sure we use the same ID for the same URL + if (URL_IDS_MAPPER.containsKey(url.toFullString())) { + ids.put(URL_IDS_MAPPER.get(url.toFullString()), url); + } else { + String md5 = MD5Util.MD5_16bit(url.toFullString()); + ids.put(md5, url); + URL_IDS_MAPPER.putIfAbsent(url.toFullString(), md5); + } + } + } + if (categories.size() == 0) { + return; + } + for (Map.Entry>> categoryEntry : categories.entrySet()) { + String category = categoryEntry.getKey(); + ConcurrentMap> services = registryCache.get(category); + if (services == null) { + services = new ConcurrentHashMap>(); + registryCache.put(category, services); + } else {// Fix map can not be cleared when service is unregistered: when a unique “group/service:version” service is unregistered, but we still have the same services with different version or group, so empty protocols can not be invoked. + Set keys = new HashSet(services.keySet()); + for (String key : keys) { + if (this.getInterface(key).equals(interfaceName) && !categoryEntry.getValue().entrySet().contains(key)) { + services.remove(key); + } + } + } + services.putAll(categoryEntry.getValue()); + } + } + + public String getInterface(String service) { + if (service != null && service.length() > 0) { + int i = service.indexOf('/'); + if (i >= 0) { + service = service.substring(i + 1); + } + i = service.lastIndexOf(':'); + if (i >= 0) { + service = service.substring(0, i); + } + } + return service; + } + + public String getGroup(String service) { + if (service != null && service.length() > 0) { + int i = service.indexOf('/'); + if (i >= 0) { + return service.substring(0, i); + } + } + return null; + } + + public String getVersion(String service) { + if (service != null && service.length() > 0) { + int i = service.lastIndexOf(':'); + if (i >= 0) { + return service.substring(i + 1); + } + } + return null; + } +} diff --git a/backend/src/main/java/io/metersphere/api/dubbo/utils/Constants.java b/backend/src/main/java/io/metersphere/api/dubbo/utils/Constants.java new file mode 100644 index 0000000000..16ff47dc56 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dubbo/utils/Constants.java @@ -0,0 +1,614 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.metersphere.api.dubbo.utils; + +import io.metersphere.api.dubbo.MethodArgument; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jmeter.testelement.property.IntegerProperty; +import org.apache.jmeter.testelement.property.StringProperty; + +import java.util.ArrayList; +import java.util.List; + +/** + * Constants + */ +public class Constants { + + //Registry Protocol + public static final String REGISTRY_NONE = "none"; + public static final String REGISTRY_ZOOKEEPER = "zookeeper"; + public static final String REGISTRY_NACOS = "nacos"; + public static final String APOLLO = "apollo"; + public static final String REGISTRY_MULTICAST = "multicast"; + public static final String REGISTRY_REDIS = "redis"; + public static final String REGISTRY_SIMPLE = "simple"; + + //RPC Protocol + public static final String RPC_PROTOCOL_DUBBO = "dubbo"; + public static final String RPC_PROTOCOL_RMI = "rmi"; + public static final String RPC_PROTOCOL_HESSIAN = "hessian"; + public static final String RPC_PROTOCOL_HTTP = "http"; + public static final String RPC_PROTOCOL_WEBSERVICE = "webservice"; + public static final String RPC_PROTOCOL_THRIFT = "thrift"; + public static final String RPC_PROTOCOL_MEMCACHED = "memcached"; + public static final String RPC_PROTOCOL_REDIS = "redis"; + + public static final String ASYNC = "async"; + public static final String SYMBOL = "://"; + + public static final int INT_DEFAULT = 0; + public static final double DOUBLE_DEFAULT = 0.0d; + public static final boolean BOOLEAN_DEFAULT = false; + public static final char CHAR_DEFAULT = '\u0000'; + public static final float FLOAT_DEFAULT = 0.0f; + public static final byte BYTE_DEFAULT = 0; + public static final long LONG_DEFAULT = 0l; + public static final short SHORT_DEFAULT = 0; + public static final int[] INT_ARRAY_DEFAULT = null; + public static final double[] DOUBLE_ARRAY_DEFAULT = null; + public static final boolean[] BOOLEAN_ARRAY_DEFAULT = null; + public static final char[] CHAT_ARRAY_DEFAULT = null; + public static final float[] FLOAT_ARRAY_DEFAULT = null; + public static final byte[] BYTE_ARRAY_DEFAULT = null; + public static final long[] LONG_ARRAY_DEFAULT = null; + public static final short[] SHORT_ARRAY_DEFAULT = null; + + public static final String FIELD_DUBBO_REGISTRY_PROTOCOL = "FIELD_DUBBO_REGISTRY_PROTOCOL"; + public static final String FIELD_DUBBO_REGISTRY_GROUP = "FIELD_DUBBO_REGISTRY_GROUP"; + public static final String FIELD_DUBBO_REGISTRY_USER_NAME = "FIELD_DUBBO_REGISTRY_USER_NAME"; + public static final String FIELD_DUBBO_REGISTRY_PASSWORD = "FIELD_DUBBO_REGISTRY_PASSWORD"; + public static final String FIELD_DUBBO_REGISTRY_TIMEOUT = "FIELD_DUBBO_REGISTRY_TIMEOUT"; + public static final String FIELD_DUBBO_CONFIG_CENTER_PROTOCOL = "FIELD_DUBBO_CONFIG_CENTER_PROTOCOL"; + public static final String FIELD_DUBBO_CONFIG_CENTER_GROUP = "FIELD_DUBBO_CONFIG_CENTER_GROUP"; + public static final String FIELD_DUBBO_CONFIG_CENTER_NAMESPACE = "FIELD_DUBBO_CONFIG_CENTER_NAMESPACE"; + public static final String FIELD_DUBBO_CONFIG_CENTER_USER_NAME = "FIELD_DUBBO_CONFIG_CENTER_USER_NAME"; + public static final String FIELD_DUBBO_CONFIG_CENTER_PASSWORD = "FIELD_DUBBO_CONFIG_CENTER_PASSWORD"; + public static final String FIELD_DUBBO_CONFIG_CENTER_TIMEOUT = "FIELD_DUBBO_CONFIG_CENTER_TIMEOUT"; + public static final String FIELD_DUBBO_CONFIG_CENTER_ADDRESS = "FIELD_DUBBO_CONFIG_CENTER_ADDRESS"; + public static final String FIELD_DUBBO_RPC_PROTOCOL = "FIELD_DUBBO_RPC_PROTOCOL"; + public static final String FIELD_DUBBO_ADDRESS = "FIELD_DUBBO_ADDRESS"; + public static final String FIELD_DUBBO_TIMEOUT = "FIELD_DUBBO_TIMEOUT"; + public static final String FIELD_DUBBO_VERSION = "FIELD_DUBBO_VERSION"; + public static final String FIELD_DUBBO_RETRIES = "FIELD_DUBBO_RETRIES"; + public static final String FIELD_DUBBO_CLUSTER = "FIELD_DUBBO_CLUSTER"; + public static final String FIELD_DUBBO_GROUP = "FIELD_DUBBO_GROUP"; + public static final String FIELD_DUBBO_CONNECTIONS = "FIELD_DUBBO_CONNECTIONS"; + public static final String FIELD_DUBBO_LOADBALANCE = "FIELD_DUBBO_LOADBALANCE"; + public static final String FIELD_DUBBO_ASYNC = "FIELD_DUBBO_ASYNC"; + public static final String FIELD_DUBBO_INTERFACE = "FIELD_DUBBO_INTERFACE"; + public static final String FIELD_DUBBO_METHOD = "FIELD_DUBBO_METHOD"; + public static final String FIELD_DUBBO_METHOD_ARGS = "FIELD_DUBBO_METHOD_ARGS"; + public static final String FIELD_DUBBO_METHOD_ARGS_SIZE = "FIELD_DUBBO_METHOD_ARGS_SIZE"; + public static final String FIELD_DUBBO_ATTACHMENT_ARGS = "FIELD_DUBBO_ATTACHMENT_ARGS"; + public static final String FIELD_DUBBO_ATTACHMENT_ARGS_SIZE = "FIELD_DUBBO_ATTACHMENT_ARGS_SIZE"; + public static final String DEFAULT_TIMEOUT = "1000"; + public static final String DEFAULT_VERSION = "1.0"; + public static final String DEFAULT_RETRIES = "0"; + public static final String DEFAULT_CLUSTER = "failfast"; + public static final String DEFAULT_CONNECTIONS = "100"; + + //冗余配置元件中的address、protocols、group,用于在sample gui获取配置元件中的默认值 + public static String DEFAULT_PANEL_ADDRESS = ""; + public static String DEFAULT_PANEL_PROTOCOLS = ""; + public static String DEFAULT_PANEL_GROUP = ""; + + public static final void redundancy(TestElement element) { + DEFAULT_PANEL_ADDRESS = Constants.getAddress(element); + DEFAULT_PANEL_PROTOCOLS = Constants.getRegistryProtocol(element); + DEFAULT_PANEL_GROUP = Constants.getRegistryGroup(element); + } + + /** + * get Registry Protocol + * + * @return the protocol + */ + public static final String getRegistryProtocol(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_REGISTRY_PROTOCOL); + } + + /** + * set Registry Protocol + * + * @param registryProtocol the protocol to set + */ + public static final void setRegistryProtocol(String registryProtocol, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_REGISTRY_PROTOCOL, StringUtils.trimAllWhitespace(registryProtocol))); + } + + /** + * get Registry Group + * + * @return the group + */ + public static final String getRegistryGroup(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_REGISTRY_GROUP); + } + + /** + * set Registry Group + * + * @param registryGroup the group to set + */ + public static final void setRegistryGroup(String registryGroup, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_REGISTRY_GROUP, StringUtils.trimAllWhitespace(registryGroup))); + } + + /** + * get Registry username + * + * @return the username + */ + public static final String getRegistryUserName(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_REGISTRY_USER_NAME); + } + + /** + * set Registry username + * + * @param username the username to set + */ + public static final void setRegistryUserName(String username, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_REGISTRY_USER_NAME, StringUtils.trimAllWhitespace(username))); + } + + /** + * get Registry password + * + * @return the password + */ + public static final String getRegistryPassword(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_REGISTRY_PASSWORD); + } + + /** + * set Registry password + * + * @param password the password to set + */ + public static final void setRegistryPassword(String password, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_REGISTRY_PASSWORD, StringUtils.trimAllWhitespace(password))); + } + + /** + * get Registry timeout + * + * @return the timeout + */ + public static final String getRegistryTimeout(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_REGISTRY_TIMEOUT); + } + + /** + * set Registry timeout + * + * @param timeout the group to set + */ + public static final void setRegistryTimeout(String timeout, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_REGISTRY_TIMEOUT, StringUtils.trimAllWhitespace(timeout))); + } + + /** + * get ConfigCenter protocol + * + * @return the protocol + */ + public static final String getConfigCenterProtocol(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_CONFIG_CENTER_PROTOCOL); + } + + /** + * set ConfigCenter protocol + * + * @param protocol the protocol to set + */ + public static final void setConfigCenterProtocol(String protocol, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_CONFIG_CENTER_PROTOCOL, StringUtils.trimAllWhitespace(protocol))); + } + + /** + * get ConfigCenter group + * + * @return the group + */ + public static final String getConfigCenterGroup(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_CONFIG_CENTER_GROUP); + } + + /** + * set ConfigCenter group + * + * @param group the group to set + */ + public static final void setConfigCenterGroup(String group, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_CONFIG_CENTER_GROUP, StringUtils.trimAllWhitespace(group))); + } + + /** + * get ConfigCenter namespace + * + * @return the namespace + */ + public static final String getConfigCenterNamespace(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_CONFIG_CENTER_NAMESPACE); + } + + /** + * set ConfigCenter namespace + * + * @param namespace the namespace to set + */ + public static final void setConfigCenterNamespace(String namespace, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_CONFIG_CENTER_NAMESPACE, StringUtils.trimAllWhitespace(namespace))); + } + + /** + * get ConfigCenter username + * + * @return the username + */ + public static final String getConfigCenterUserName(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_CONFIG_CENTER_USER_NAME); + } + + /** + * set ConfigCenter username + * + * @param username the username to set + */ + public static final void setConfigCenterUserName(String username, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_CONFIG_CENTER_USER_NAME, StringUtils.trimAllWhitespace(username))); + } + + /** + * get ConfigCenter password + * + * @return the password + */ + public static final String getConfigCenterPassword(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_CONFIG_CENTER_PASSWORD); + } + + /** + * set ConfigCenter password + * + * @param password the password to set + */ + public static final void setConfigCenterPassword(String password, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_CONFIG_CENTER_PASSWORD, StringUtils.trimAllWhitespace(password))); + } + + /** + * get ConfigCenter address + * + * @return the address + */ + public static final String getConfigCenterAddress(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_CONFIG_CENTER_ADDRESS); + } + + /** + * set ConfigCenter namespace + * + * @param address the address to set + */ + public static final void setConfigCenterAddress(String address, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_CONFIG_CENTER_ADDRESS, StringUtils.trimAllWhitespace(address))); + } + + /** + * get ConfigCenter timeout + * + * @return the timeout + */ + public static final String getConfigCenterTimeout(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_CONFIG_CENTER_TIMEOUT); + } + + /** + * set ConfigCenter namespace + * + * @param timeout the timeout to set + */ + public static final void setConfigCenterTimeout(String timeout, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_CONFIG_CENTER_TIMEOUT, StringUtils.trimAllWhitespace(timeout))); + } + + /** + * get RPC protocol + * + * @return the RPC protocol + */ + public static final String getRpcProtocol(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_RPC_PROTOCOL); + } + + /** + * set RPC protocol + * + * @param rpcProtocol the protocol to set + */ + public static final void setRpcProtocol(String rpcProtocol, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_RPC_PROTOCOL, StringUtils.trimAllWhitespace(rpcProtocol))); + } + + /** + * get address + * + * @return the address + */ + public static final String getAddress(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_ADDRESS); + } + + /** + * set address + * + * @param address the address to set + */ + public static final void setAddress(String address, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_ADDRESS, StringUtils.trimAllWhitespace(address))); + } + + /** + * get timeout + * + * @return the timeout + */ + public static final String getTimeout(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_TIMEOUT, DEFAULT_TIMEOUT); + } + + /** + * set timeout + * + * @param timeout the timeout to set + */ + public static final void setTimeout(String timeout, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_TIMEOUT, StringUtils.trimAllWhitespace(timeout))); + } + + /** + * get version + * + * @return the version + */ + public static final String getVersion(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_VERSION, DEFAULT_VERSION); + } + + /** + * set version + * + * @param version the version to set + */ + public static final void setVersion(String version, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_VERSION, StringUtils.trimAllWhitespace(version))); + } + + /** + * get retries + * + * @return the retries + */ + public static final String getRetries(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_RETRIES, DEFAULT_RETRIES); + } + + /** + * set retries + * + * @param retries the retries to set + */ + public static final void setRetries(String retries, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_RETRIES, StringUtils.trimAllWhitespace(retries))); + } + + /** + * get cluster + * + * @return the cluster + */ + public static final String getCluster(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_CLUSTER, DEFAULT_CLUSTER); + } + + /** + * set cluster + * + * @param cluster the cluster to set + */ + public static final void setCluster(String cluster, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_CLUSTER, StringUtils.trimAllWhitespace(cluster))); + } + + /** + * get group + * + * @return the group + */ + public static final String getGroup(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_GROUP, null); + } + + /** + * set group + * + * @param group the group to set + */ + public static final void setGroup(String group, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_GROUP, StringUtils.trimAllWhitespace(group))); + } + + /** + * get connections + * + * @return the group + */ + public static final String getConnections(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_CONNECTIONS, DEFAULT_CONNECTIONS); + } + + /** + * set connections + * + * @param connections the connections to set + */ + public static final void setConnections(String connections, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_CONNECTIONS, StringUtils.trimAllWhitespace(connections))); + } + + /** + * get loadbalance + * + * @return the loadbalance + */ + public static final String getLoadbalance(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_LOADBALANCE); + } + + /** + * set loadbalance + * + * @param loadbalance the loadbalance to set + */ + public static final void setLoadbalance(String loadbalance, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_LOADBALANCE, StringUtils.trimAllWhitespace(loadbalance))); + } + + /** + * get async + * + * @return the async + */ + public static final String getAsync(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_ASYNC); + } + + /** + * set async + * + * @param async the async to set + */ + public static final void setAsync(String async, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_ASYNC, StringUtils.trimAllWhitespace(async))); + } + + /** + * get interfaceName + * + * @return the interfaceName + */ + public static final String getInterface(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_INTERFACE); + } + + /** + * set interfaceName + * + * @param interfaceName the interfaceName to set + */ + public static final void setInterfaceName(String interfaceName, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_INTERFACE, StringUtils.trimAllWhitespace(interfaceName))); + } + + /** + * get method + * + * @return the method + */ + public static final String getMethod(TestElement element) { + return element.getPropertyAsString(FIELD_DUBBO_METHOD); + } + + /** + * set method + * + * @param method the method to set + */ + public static final void setMethod(String method, TestElement element) { + element.setProperty(new StringProperty(FIELD_DUBBO_METHOD, StringUtils.trimAllWhitespace(method))); + } + + /** + * get methodArgs + * + * @return the methodArgs + */ + public static final List getMethodArgs(TestElement element) { + int paramsSize = element.getPropertyAsInt(FIELD_DUBBO_METHOD_ARGS_SIZE, 0); + List list = new ArrayList(); + for (int i = 1; i <= paramsSize; i++) { + String paramType = element.getPropertyAsString(FIELD_DUBBO_METHOD_ARGS + "_PARAM_TYPE" + i); + String paramValue = element.getPropertyAsString(FIELD_DUBBO_METHOD_ARGS + "_PARAM_VALUE" + i); + MethodArgument args = new MethodArgument(paramType, paramValue); + list.add(args); + } + return list; + } + + /** + * set methodArgs + * + * @param methodArgs the methodArgs to set + */ + public static final void setMethodArgs(List methodArgs, TestElement element) { + int size = methodArgs == null ? 0 : methodArgs.size(); + element.setProperty(new IntegerProperty(FIELD_DUBBO_METHOD_ARGS_SIZE, size)); + if (size > 0) { + for (int i = 1; i <= methodArgs.size(); i++) { + element.setProperty(new StringProperty(FIELD_DUBBO_METHOD_ARGS + "_PARAM_TYPE" + i, methodArgs.get(i - 1).getParamType())); + element.setProperty(new StringProperty(FIELD_DUBBO_METHOD_ARGS + "_PARAM_VALUE" + i, methodArgs.get(i - 1).getParamValue())); + } + } + } + + /** + * get attachmentArgs + * + * @return the attachmentArgs + */ + public static final List getAttachmentArgs(TestElement element) { + int paramsSize = element.getPropertyAsInt(FIELD_DUBBO_ATTACHMENT_ARGS_SIZE, 0); + List list = new ArrayList(); + for (int i = 1; i <= paramsSize; i++) { + String paramType = element.getPropertyAsString(FIELD_DUBBO_ATTACHMENT_ARGS + "_KEY" + i); + String paramValue = element.getPropertyAsString(FIELD_DUBBO_ATTACHMENT_ARGS + "_VALUE" + i); + MethodArgument args = new MethodArgument(paramType, paramValue); + list.add(args); + } + return list; + } + + /** + * set attachmentArgs + * + * @param methodArgs the attachmentArgs to set + */ + public static final void setAttachmentArgs(List methodArgs, TestElement element) { + int size = methodArgs == null ? 0 : methodArgs.size(); + element.setProperty(new IntegerProperty(FIELD_DUBBO_ATTACHMENT_ARGS_SIZE, size)); + if (size > 0) { + for (int i = 1; i <= methodArgs.size(); i++) { + element.setProperty(new StringProperty(FIELD_DUBBO_ATTACHMENT_ARGS + "_KEY" + i, methodArgs.get(i - 1).getParamType())); + element.setProperty(new StringProperty(FIELD_DUBBO_ATTACHMENT_ARGS + "_VALUE" + i, methodArgs.get(i - 1).getParamValue())); + } + } + } + +} diff --git a/backend/src/main/java/io/metersphere/api/dubbo/utils/JsonUtils.java b/backend/src/main/java/io/metersphere/api/dubbo/utils/JsonUtils.java new file mode 100644 index 0000000000..e3022ab121 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dubbo/utils/JsonUtils.java @@ -0,0 +1,52 @@ +package io.metersphere.api.dubbo.utils; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonSyntaxException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Type; + +/** + * JsonUtils + */ +public class JsonUtils { + + private static final Logger logger = LoggerFactory.getLogger(JsonUtils.class); + + private static final Gson gson = new GsonBuilder() + .setDateFormat("yyyy-MM-dd HH:mm:ss") + .setPrettyPrinting() + .disableHtmlEscaping() + .serializeNulls() + .create(); + + public static String toJson(Object obj) { + return gson.toJson(obj); + } + + public static String toJson(Object obj, Type type) { + return gson.toJson(obj, type); + } + + public static T formJson(String json, Class classOfT) { + try { + return gson.fromJson(json, classOfT); + } catch (JsonSyntaxException e) { + logger.error("json to class[" + classOfT.getName() + "] is error!", + e); + } + return null; + } + + public static T formJson(String json, Type type) { + try { + return gson.fromJson(json, type); + } catch (JsonSyntaxException e) { + logger.error("json to class[" + type.getClass().getName() + + "] is error!", e); + } + return null; + } +} diff --git a/backend/src/main/java/io/metersphere/api/dubbo/utils/MD5Util.java b/backend/src/main/java/io/metersphere/api/dubbo/utils/MD5Util.java new file mode 100644 index 0000000000..23786ab5fd --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dubbo/utils/MD5Util.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.metersphere.api.dubbo.utils; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * MD5Util + */ +public class MD5Util { + private static MessageDigest md; + private static final char[] hexCode = "0123456789ABCDEF".toCharArray(); + + static { + try { + md = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + } + + public static String MD5_16bit(String input) { + String hash = MD5_32bit(input); + if (hash == null) { + return null; + } + return hash.substring(8, 24); + } + + public static String MD5_32bit(String input) { + if (input == null || input.length() == 0) { + return null; + } + md.update(input.getBytes()); + byte[] digest = md.digest(); + String hash = convertToString(digest); + return hash; + } + + private static String convertToString(byte[] data) { + StringBuilder r = new StringBuilder(data.length * 2); + for (byte b : data) { + r.append(hexCode[(b >> 4) & 0xF]); + r.append(hexCode[(b & 0xF)]); + } + return r.toString(); + } + +} diff --git a/backend/src/main/java/io/metersphere/api/dubbo/utils/StringUtils.java b/backend/src/main/java/io/metersphere/api/dubbo/utils/StringUtils.java new file mode 100644 index 0000000000..5f9f4814d2 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dubbo/utils/StringUtils.java @@ -0,0 +1,69 @@ +package io.metersphere.api.dubbo.utils; + +/** + * StringUtils + */ +public class StringUtils { + + public static boolean hasLength(String str) { + return str != null && !str.isEmpty(); + } + + public static String trimAllWhitespace(String str) { + if (!hasLength(str)) { + return str; + } else { + int len = str.length(); + StringBuilder sb = new StringBuilder(str.length()); + + for (int i = 0; i < len; ++i) { + char c = str.charAt(i); + if (!Character.isWhitespace(c)) { + sb.append(c); + } + } + + return sb.toString(); + } + } + + public static String trimWhitespace(String str) { + if (!hasLength(str)) { + return str; + } else { + StringBuilder sb = new StringBuilder(str); + + while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) { + sb.deleteCharAt(0); + } + + while (sb.length() > 0 && Character.isWhitespace(sb.charAt(sb.length() - 1))) { + sb.deleteCharAt(sb.length() - 1); + } + + return sb.toString(); + } + } + + public static boolean isBlank(CharSequence cs) { + int strLen; + if (cs != null && (strLen = cs.length()) != 0) { + for (int i = 0; i < strLen; ++i) { + if (!Character.isWhitespace(cs.charAt(i))) { + return false; + } + } + + return true; + } else { + return true; + } + } + + public static boolean isBlank1(String paramValue) { + if (isBlank(paramValue) || "null".equals(paramValue.toLowerCase())) { + return true; + } + return false; + } +} diff --git a/backend/src/main/java/io/metersphere/api/parse/MsParser.java b/backend/src/main/java/io/metersphere/api/parse/MsParser.java index cf2e006a28..e83176b1c9 100644 --- a/backend/src/main/java/io/metersphere/api/parse/MsParser.java +++ b/backend/src/main/java/io/metersphere/api/parse/MsParser.java @@ -1,7 +1,12 @@ package io.metersphere.api.parse; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; import io.metersphere.api.dto.parse.ApiImport; +import io.metersphere.commons.constants.MsRequestBodyType; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.jetty.http.HttpMethod; import java.io.InputStream; @@ -10,7 +15,44 @@ public class MsParser extends ApiImportAbstractParser { @Override public ApiImport parse(InputStream source) { String testStr = getApiTestStr(source); - return JSON.parseObject(testStr, ApiImport.class); + return JSON.parseObject(parsePluginFormat(testStr), ApiImport.class); } + private String parsePluginFormat(String testStr) { + JSONObject testObject = JSONObject.parseObject(testStr); + if (testObject.get("scenarios") != null) { + return testStr; + } else { + //插件格式 + JSONArray scenarios = new JSONArray(); + testObject.keySet().forEach(scenarioName -> { + JSONObject scenario = new JSONObject(); + scenario.put("name", scenarioName); + JSONArray requestsObjects = new JSONArray(); + JSONObject requestsObject = testObject.getJSONObject(scenarioName); + requestsObject.keySet().forEach(requestName -> { + JSONObject requestObject = requestsObject.getJSONObject(requestName); + requestObject.put("name", requestName); + JSONArray bodies = requestObject.getJSONArray("body"); + if (StringUtils.equalsIgnoreCase(requestObject.getString("method"), HttpMethod.POST.name()) && bodies != null) { + StringBuilder bodyStr = new StringBuilder(); + for (int i = 0; i < bodies.size(); i++) { + String body = bodies.getString(i); + bodyStr.append(body); + } + JSONObject bodyObject = new JSONObject(); + bodyObject.put("raw", bodyStr); + bodyObject.put("type", MsRequestBodyType.RAW.value()); + requestObject.put("body", bodyObject); + } + requestsObjects.add(requestObject); + }); + scenario.put("requests", requestsObjects); + scenarios.add(scenario); + }); + JSONObject result = new JSONObject(); + result.put("scenarios", scenarios); + return result.toJSONString(); + } + } } diff --git a/backend/src/main/java/io/metersphere/api/parse/PostmanParser.java b/backend/src/main/java/io/metersphere/api/parse/PostmanParser.java index ed270d27b9..8f3988fb3a 100644 --- a/backend/src/main/java/io/metersphere/api/parse/PostmanParser.java +++ b/backend/src/main/java/io/metersphere/api/parse/PostmanParser.java @@ -14,10 +14,23 @@ import org.apache.commons.lang3.StringUtils; import java.io.InputStream; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class PostmanParser extends ApiImportAbstractParser { + private static Map postmanBodyRowMap; + + static { + postmanBodyRowMap = new HashMap<>(); + postmanBodyRowMap.put("json", "application/json"); + postmanBodyRowMap.put("text", "text/plain"); + postmanBodyRowMap.put("html", "text/html"); + postmanBodyRowMap.put("xml", "text/xml"); + postmanBodyRowMap.put("javascript", "application/x-javascript"); + } + @Override public ApiImport parse(InputStream source) { String testStr = getApiTestStr(source); @@ -69,7 +82,7 @@ public class PostmanParser extends ApiImportAbstractParser { if (StringUtils.equals(bodyMode, PostmanRequestBodyMode.RAW.value())) { body.setRaw(postmanBody.getString(bodyMode)); body.setType(MsRequestBodyType.RAW.value()); - String contentType = postmanBody.getJSONObject("options").getJSONObject("raw").getString("language"); + String contentType = postmanBodyRowMap.get(postmanBody.getJSONObject("options").getJSONObject("raw").getString("language")); addContentType(request, contentType); } else if (StringUtils.equals(bodyMode, PostmanRequestBodyMode.FORM_DATA.value()) || StringUtils.equals(bodyMode, PostmanRequestBodyMode.URLENCODED.value())) { List postmanKeyValues = JSON.parseArray(postmanBody.getString(bodyMode), PostmanKeyValue.class); diff --git a/backend/src/main/java/io/metersphere/api/service/APITestService.java b/backend/src/main/java/io/metersphere/api/service/APITestService.java index 7f49463951..a41addfd2e 100644 --- a/backend/src/main/java/io/metersphere/api/service/APITestService.java +++ b/backend/src/main/java/io/metersphere/api/service/APITestService.java @@ -31,6 +31,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.web.multipart.MultipartFile; +import javax.annotation.Resource; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.List; @@ -39,8 +40,6 @@ import java.util.Random; import java.util.UUID; import java.util.stream.Collectors; -import javax.annotation.Resource; - @Service @Transactional(rollbackFor = Exception.class) public class APITestService { @@ -76,6 +75,7 @@ public class APITestService { if (files == null || files.isEmpty()) { throw new IllegalArgumentException(Translator.get("file_cannot_be_null")); } + checkNameExist(request); ApiTest test = createTest(request); saveFile(test.getId(), files); } @@ -173,12 +173,6 @@ public class APITestService { } } - private Boolean isNameExist(SaveAPITestRequest request) { - ApiTestExample example = new ApiTestExample(); - example.createCriteria().andNameEqualTo(request.getName()).andProjectIdEqualTo(request.getProjectId()).andIdNotEqualTo(request.getId()); - return apiTestMapper.countByExample(example) > 0; - } - private ApiTest updateTest(SaveAPITestRequest request) { checkNameExist(request); final ApiTest test = new ApiTest(); @@ -193,7 +187,6 @@ public class APITestService { } private ApiTest createTest(SaveAPITestRequest request) { - checkNameExist(request); final ApiTest test = new ApiTest(); test.setId(request.getId()); test.setName(request.getName()); @@ -290,10 +283,6 @@ public class APITestService { request.setName(name.substring(0, name.length() - suffix.length())); } } - - if (isNameExist(request)) { - request.setName(request.getName() + "_" + request.getId().substring(0, 5)); - } return request; } } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiTestEnvironmentService.java b/backend/src/main/java/io/metersphere/api/service/ApiTestEnvironmentService.java index e2e485ae2a..acaa4e3551 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiTestEnvironmentService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiTestEnvironmentService.java @@ -34,7 +34,7 @@ public class ApiTestEnvironmentService { } public void update(ApiTestEnvironmentWithBLOBs apiTestEnvironment) { - apiTestEnvironmentMapper.updateByPrimaryKeySelective(apiTestEnvironment); + apiTestEnvironmentMapper.updateByPrimaryKey(apiTestEnvironment); } public String add(ApiTestEnvironmentWithBLOBs apiTestEnvironmentWithBLOBs) { diff --git a/backend/src/main/java/io/metersphere/performance/engine/docker/DockerTestEngine.java b/backend/src/main/java/io/metersphere/performance/engine/docker/DockerTestEngine.java index 41964c9967..4519e5916e 100644 --- a/backend/src/main/java/io/metersphere/performance/engine/docker/DockerTestEngine.java +++ b/backend/src/main/java/io/metersphere/performance/engine/docker/DockerTestEngine.java @@ -111,7 +111,7 @@ public class DockerTestEngine extends AbstractEngine { restTemplateWithTimeOut.getForObject(uri, String.class); } catch (Exception e) { LogUtil.error("stop load test fail... " + testId); - MSException.throwException(Translator.get("delete_fail")); + MSException.throwException(Translator.get("container_delete_fail")); } }); } diff --git a/backend/src/main/resources/i18n/messages_en_US.properties b/backend/src/main/resources/i18n/messages_en_US.properties index 3235997a3b..c5fa1e1c84 100644 --- a/backend/src/main/resources/i18n/messages_en_US.properties +++ b/backend/src/main/resources/i18n/messages_en_US.properties @@ -46,6 +46,7 @@ max_thread_insufficient=The number of concurrent users exceeds related_case_del_fail_prefix=Connected to related_case_del_fail_suffix=TestCase, please disassociate first jmx_content_valid=JMX content is invalid +container_delete_fail=The container failed to stop, please try again #workspace workspace_name_is_null=Workspace name cannot be null workspace_name_already_exists=The workspace name already exists diff --git a/backend/src/main/resources/i18n/messages_zh_CN.properties b/backend/src/main/resources/i18n/messages_zh_CN.properties index 45c659347c..fca626535e 100644 --- a/backend/src/main/resources/i18n/messages_zh_CN.properties +++ b/backend/src/main/resources/i18n/messages_zh_CN.properties @@ -46,6 +46,7 @@ max_thread_insufficient=并发用户数超额 related_case_del_fail_prefix=已关联到 related_case_del_fail_suffix=测试用例,请先解除关联 jmx_content_valid=JMX 内容无效,请检查 +container_delete_fail=容器停止失败,请重试 #workspace workspace_name_is_null=工作空间名不能为空 workspace_name_already_exists=工作空间名已存在 diff --git a/backend/src/main/resources/i18n/messages_zh_TW.properties b/backend/src/main/resources/i18n/messages_zh_TW.properties index 61d3091cd7..3c8ede8304 100644 --- a/backend/src/main/resources/i18n/messages_zh_TW.properties +++ b/backend/src/main/resources/i18n/messages_zh_TW.properties @@ -46,6 +46,7 @@ max_thread_insufficient=並發用戶數超額 related_case_del_fail_prefix=已關聯到 related_case_del_fail_suffix=測試用例,請先解除關聯 jmx_content_valid=JMX 內容無效,請檢查 +container_delete_fail=容器停止失敗,請重試 #workspace workspace_name_is_null=工作空間名不能為空 workspace_name_already_exists=工作空間名已存在 diff --git a/frontend/src/business/components/api/test/ApiTestConfig.vue b/frontend/src/business/components/api/test/ApiTestConfig.vue index 080c03aecb..5d474723c5 100644 --- a/frontend/src/business/components/api/test/ApiTestConfig.vue +++ b/frontend/src/business/components/api/test/ApiTestConfig.vue @@ -255,7 +255,7 @@ return this.test.isValid() && !this.change; }, isDisabled() { - return !(this.test.isValid()) + return !(this.test.isValid() && this.change); } }, diff --git a/frontend/src/business/components/api/test/components/ApiEnvironmentConfig.vue b/frontend/src/business/components/api/test/components/ApiEnvironmentConfig.vue index 277ed420de..033d279310 100644 --- a/frontend/src/business/components/api/test/components/ApiEnvironmentConfig.vue +++ b/frontend/src/business/components/api/test/components/ApiEnvironmentConfig.vue @@ -3,7 +3,7 @@ - + @@ -50,19 +50,34 @@ this.getEnvironments(); }, deleteEnvironment(environment) { - this.result = this.$get('/api/environment/delete/' + environment.id, response => { - this.$success(this.$t('commons.delete_success')); - this.getEnvironments(); - }); + if (environment.id) { + this.result = this.$get('/api/environment/delete/' + environment.id, () => { + this.$success(this.$t('commons.delete_success')); + this.getEnvironments(); + }); + } }, copyEnvironment(environment) { let newEnvironment = {}; Object.assign(newEnvironment, environment); newEnvironment.id = null; + newEnvironment.name = this.getNoRepeatName(newEnvironment.name); + this.$refs.environmentEdit._save(newEnvironment); this.environments.push(newEnvironment); + this.$refs.environmentItems.itemSelected(this.environments.length -1 , newEnvironment); + }, + getNoRepeatName(name) { + for (let i in this.environments) { + if (this.environments[i].name === name) { + return this.getNoRepeatName(name + ' copy'); + } + } + return name; }, addEnvironment() { - this.environments.push(this.getDefaultEnvironment()); + let newEnvironment = this.getDefaultEnvironment(); + this.environments.push(newEnvironment); + this.$refs.environmentItems.itemSelected(this.environments.length -1 , newEnvironment); }, environmentSelected(environment) { this.getEnvironment(environment); @@ -96,6 +111,8 @@ }, close() { this.$emit('close'); + this.visible = false; + this.$refs.environmentEdit.clearValidate(); } } } @@ -115,7 +132,4 @@ height: 100%; position: absolute; } - - - diff --git a/frontend/src/business/components/api/test/components/ApiRequestForm.vue b/frontend/src/business/components/api/test/components/ApiRequestForm.vue index c8b268cf2f..56a6c60292 100644 --- a/frontend/src/business/components/api/test/components/ApiRequestForm.vue +++ b/frontend/src/business/components/api/test/components/ApiRequestForm.vue @@ -2,7 +2,7 @@ - + @@ -91,7 +91,7 @@ activeName: "parameters", rules: { name: [ - {max: 100, message: this.$t('commons.input_limit', [1, 100]), trigger: 'blur'} + {max: 300, message: this.$t('commons.input_limit', [1, 300]), trigger: 'blur'} ], url: [ {max: 500, required: true, message: this.$t('commons.input_limit', [1, 500]), trigger: 'blur'}, diff --git a/frontend/src/business/components/api/test/components/ApiScenarioForm.vue b/frontend/src/business/components/api/test/components/ApiScenarioForm.vue index 383a82e190..deb002d984 100644 --- a/frontend/src/business/components/api/test/components/ApiScenarioForm.vue +++ b/frontend/src/business/components/api/test/components/ApiScenarioForm.vue @@ -16,10 +16,6 @@ - - - - @@ -86,6 +82,7 @@ for (let i in this.environments) { if (this.environments[i].id === this.scenario.environmentId) { this.scenario.environment = this.environments[i]; + this.setRequestEnvironments(); hasEnvironment = true; break; } @@ -104,9 +101,7 @@ for (let i in this.environments) { if (this.environments[i].id === value) { this.scenario.environment = this.environments[i]; - this.scenario.requests.forEach(request => { - request.environment = this.environments[i]; - }); + this.setRequestEnvironments(); break; } } @@ -126,6 +121,11 @@ }, environmentConfigClose() { this.getEnvironments(); + }, + setRequestEnvironments() { + this.scenario.requests.forEach(request => { + request.environment = this.scenario.environment; + }); } } } diff --git a/frontend/src/business/components/api/test/components/environment/EnvironmentEdit.vue b/frontend/src/business/components/api/test/components/environment/EnvironmentEdit.vue index ed2261eaed..3b3380c425 100644 --- a/frontend/src/business/components/api/test/components/environment/EnvironmentEdit.vue +++ b/frontend/src/business/components/api/test/components/environment/EnvironmentEdit.vue @@ -25,10 +25,12 @@ {{$t('api_test.request.headers')}} - + @@ -38,10 +40,12 @@ diff --git a/frontend/src/business/components/common/components/MsAsideItem.vue b/frontend/src/business/components/common/components/MsAsideItem.vue index 48b0f33edb..2f37bbe9c7 100644 --- a/frontend/src/business/components/common/components/MsAsideItem.vue +++ b/frontend/src/business/components/common/components/MsAsideItem.vue @@ -10,14 +10,10 @@ -
- - - - + - +