build: 动态解析插件数据
This commit is contained in:
parent
931c7c7e1b
commit
4610d0919e
|
@ -0,0 +1,12 @@
|
||||||
|
package io.metersphere.plugin.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface PluginSubType {
|
||||||
|
String value();
|
||||||
|
}
|
|
@ -4,25 +4,22 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
import io.metersphere.plugin.util.PluginLogUtils;
|
import io.metersphere.plugin.util.PluginLogUtils;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import org.apache.jmeter.save.SaveService;
|
import org.apache.jmeter.save.SaveService;
|
||||||
import org.apache.jorphan.collections.HashTree;
|
import org.apache.jorphan.collections.HashTree;
|
||||||
import org.apache.jorphan.collections.ListedHashTree;
|
import org.apache.jorphan.collections.ListedHashTree;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "clazzName")
|
|
||||||
@Data
|
@Data
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public abstract class TestElementDTO {
|
public abstract class TestElementDTO implements Serializable {
|
||||||
// 组件类型
|
// 组件类型
|
||||||
private String type;
|
private String type;
|
||||||
|
|
||||||
// 用于数据反射对象
|
|
||||||
private String clazzName = TestElementDTO.class.getCanonicalName();
|
|
||||||
|
|
||||||
// 当前组件唯一标示
|
// 当前组件唯一标示
|
||||||
private String uuid;
|
private String uuid;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package io.metersphere.api.dto.jmeter.processors;
|
||||||
|
|
||||||
|
import io.metersphere.plugin.annotation.PluginSubType;
|
||||||
|
import io.metersphere.plugin.api.dto.BaseConfigDTO;
|
||||||
|
import io.metersphere.plugin.api.dto.TestElementDTO;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.apache.jorphan.collections.HashTree;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@PluginSubType("MSJSR223Processor")
|
||||||
|
public class MSJSR223Processor extends TestElementDTO {
|
||||||
|
private String script;
|
||||||
|
private String scriptLanguage;
|
||||||
|
private Boolean jsrEnable;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void toHashTree(HashTree tree, List<TestElementDTO> hashTree, BaseConfigDTO baseConfig) {
|
||||||
|
if (!this.isEnable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package io.metersphere.api.dto.jmeter.sampler;
|
||||||
|
|
||||||
|
import io.metersphere.plugin.annotation.PluginSubType;
|
||||||
|
import io.metersphere.plugin.api.dto.BaseConfigDTO;
|
||||||
|
import io.metersphere.plugin.api.dto.TestElementDTO;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.jmeter.sampler.DebugSampler;
|
||||||
|
import org.apache.jmeter.save.SaveService;
|
||||||
|
import org.apache.jmeter.testelement.TestElement;
|
||||||
|
import org.apache.jorphan.collections.HashTree;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@PluginSubType("MSDebugSampler")
|
||||||
|
public class MSDebugSampler extends TestElementDTO {
|
||||||
|
private boolean displayJMeterProperties = false;
|
||||||
|
private boolean displayJMeterVariables = true;
|
||||||
|
private boolean displaySystemProperties = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void toHashTree(HashTree tree, List<TestElementDTO> hashTree, BaseConfigDTO configDTO) {
|
||||||
|
BaseConfigDTO config = (BaseConfigDTO) configDTO;
|
||||||
|
// 非导出操作,且不是启用状态则跳过执行
|
||||||
|
if (!this.isEnable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final HashTree groupTree = tree.add(debugSampler());
|
||||||
|
if (CollectionUtils.isNotEmpty(hashTree)) {
|
||||||
|
hashTree.forEach(el -> {
|
||||||
|
// 给所有孩子加一个父亲标志
|
||||||
|
el.setParent(this);
|
||||||
|
el.toHashTree(groupTree, el.getHashTree(), config);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private DebugSampler debugSampler() {
|
||||||
|
DebugSampler debugSampler = new DebugSampler();
|
||||||
|
debugSampler.setEnabled(this.isEnable());
|
||||||
|
if (StringUtils.isEmpty(this.getName())) {
|
||||||
|
this.setName(MSDebugSampler.class.getSimpleName());
|
||||||
|
}
|
||||||
|
debugSampler.setName(this.getName());
|
||||||
|
debugSampler.setProperty(TestElement.TEST_CLASS, DebugSampler.class.getName());
|
||||||
|
debugSampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
|
||||||
|
|
||||||
|
debugSampler.setDisplaySystemProperties(this.displaySystemProperties);
|
||||||
|
debugSampler.setDisplayJMeterVariables(this.displayJMeterVariables);
|
||||||
|
debugSampler.setDisplayJMeterProperties(this.displayJMeterProperties);
|
||||||
|
|
||||||
|
//上面三行直接Set属性会导致DebugSampler构建时取不到值,可能是JMeter的Bug,需要SetProperty
|
||||||
|
debugSampler.setProperty("displayJMeterProperties", this.displayJMeterProperties);
|
||||||
|
debugSampler.setProperty("displayJMeterVariables", this.displayJMeterVariables);
|
||||||
|
debugSampler.setProperty("displaySystemProperties", this.displaySystemProperties);
|
||||||
|
return debugSampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
package io.metersphere.api.util;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
|
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.jsontype.NamedType;
|
||||||
|
import com.fasterxml.jackson.databind.jsontype.impl.StdSubtypeResolver;
|
||||||
|
import com.fasterxml.jackson.databind.type.CollectionType;
|
||||||
|
import io.metersphere.api.dto.jmeter.processors.MSJSR223Processor;
|
||||||
|
import io.metersphere.api.dto.jmeter.sampler.MSDebugSampler;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class JSONUtils {
|
||||||
|
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
private static StdSubtypeResolver resolver = new StdSubtypeResolver();
|
||||||
|
|
||||||
|
static {
|
||||||
|
// 添加处理资源文件的类
|
||||||
|
final List<NamedType> namedTypes = new LinkedList<>();
|
||||||
|
namedTypes.add(new NamedType(MSJSR223Processor.class, MSJSR223Processor.class.getSimpleName()));
|
||||||
|
namedTypes.add(new NamedType(MSDebugSampler.class, MSDebugSampler.class.getSimpleName()));
|
||||||
|
|
||||||
|
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||||
|
// 自动检测所有类的全部属性
|
||||||
|
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
|
||||||
|
// 如果一个对象中没有任何的属性,那么在序列化的时候就会报错
|
||||||
|
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
|
||||||
|
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
|
||||||
|
namedTypes.forEach(namedType -> resolver.registerSubtypes(namedType));
|
||||||
|
objectMapper.setSubtypeResolver(resolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toJSONString(Object value) {
|
||||||
|
try {
|
||||||
|
return objectMapper.writeValueAsString(value);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object parseObject(String content) {
|
||||||
|
return parseObject(content, Object.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T parseObject(String content, Class<T> valueType) {
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(content, valueType);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T parseObject(InputStream src, Class<T> valueType) {
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(src, valueType);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T parseObject(String content, TypeReference<T> valueType) {
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(content, valueType);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List parseArray(String content) {
|
||||||
|
return parseArray(content, Object.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> List<T> parseArray(String content, Class<T> valueType) {
|
||||||
|
CollectionType javaType = objectMapper.getTypeFactory().constructCollectionType(List.class, valueType);
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(content, javaType);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置动态加载的jar的Resolver
|
||||||
|
*
|
||||||
|
* @param namedTypes
|
||||||
|
*/
|
||||||
|
public static void setResolver(List<NamedType> namedTypes) {
|
||||||
|
namedTypes.forEach(namedType -> resolver.registerSubtypes(namedType));
|
||||||
|
objectMapper.setSubtypeResolver(resolver);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package io.metersphere.api.controller;
|
||||||
|
|
||||||
|
import io.metersphere.api.dto.jmeter.processors.MSJSR223Processor;
|
||||||
|
import io.metersphere.api.dto.jmeter.sampler.MSDebugSampler;
|
||||||
|
import io.metersphere.api.util.JSONUtils;
|
||||||
|
import io.metersphere.plugin.api.dto.TestElementDTO;
|
||||||
|
import org.junit.jupiter.api.*;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||||
|
@AutoConfigureMockMvc
|
||||||
|
public class PluginSubTypeTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(0)
|
||||||
|
public void pluginSubTypeTest() throws Exception {
|
||||||
|
MSDebugSampler debugSampler = new MSDebugSampler();
|
||||||
|
debugSampler.setName("测试DebugSampler");
|
||||||
|
debugSampler.setUuid(UUID.randomUUID().toString());
|
||||||
|
LinkedList<TestElementDTO> hashTree = new LinkedList<>();
|
||||||
|
hashTree.add(debugSampler);
|
||||||
|
MSJSR223Processor msjsr223Processor = new MSJSR223Processor();
|
||||||
|
msjsr223Processor.setName("测试jsr223");
|
||||||
|
msjsr223Processor.setJsrEnable(true);
|
||||||
|
msjsr223Processor.setHashTree(hashTree);
|
||||||
|
|
||||||
|
String json = JSONUtils.toJSONString(msjsr223Processor);
|
||||||
|
Assertions.assertNotNull(json);
|
||||||
|
TestElementDTO testElementDTO = JSONUtils.parseObject(json, TestElementDTO.class);
|
||||||
|
Assertions.assertNotNull(testElementDTO);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue