feat(接口测试): 获取插件表单下拉框选项接口
This commit is contained in:
parent
785bfa10b0
commit
d6f633062b
|
@ -0,0 +1,26 @@
|
|||
package io.metersphere.plugin.api.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author wx
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
public class ApiPluginOptionsRequest implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
/**
|
||||
* 方法所需的查询参数
|
||||
* 例如:
|
||||
* 查询的过滤条件
|
||||
* 级联的情况,需要表单的其他参数值等
|
||||
*/
|
||||
private Object queryParam;
|
||||
/**
|
||||
* 需要调用的插件方法
|
||||
*/
|
||||
private String optionMethod;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package io.metersphere.plugin.api.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @Author: jianxing
|
||||
* @CreateTime: 2024-01-24 17:47
|
||||
*/
|
||||
@Data
|
||||
public class ApiPluginSelectOption {
|
||||
/**
|
||||
* 选项名称
|
||||
*/
|
||||
private String text;
|
||||
/**
|
||||
* 选项值
|
||||
*/
|
||||
private String value;
|
||||
}
|
|
@ -1,9 +1,27 @@
|
|||
package io.metersphere.plugin.api.spi;
|
||||
|
||||
import io.metersphere.plugin.api.dto.ApiPluginOptionsRequest;
|
||||
import io.metersphere.plugin.api.dto.ApiPluginSelectOption;
|
||||
import io.metersphere.plugin.sdk.spi.AbstractMsPlugin;
|
||||
import io.metersphere.plugin.sdk.util.MSPluginException;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 接口插件抽象类
|
||||
*/
|
||||
public abstract class AbstractApiPlugin extends AbstractMsPlugin {
|
||||
|
||||
public List<ApiPluginSelectOption> getPluginOptions(ApiPluginOptionsRequest request) {
|
||||
String method = request.getOptionMethod();
|
||||
try {
|
||||
// 这里反射调用插件方法,获取下拉框选项
|
||||
return (List<ApiPluginSelectOption>) this.getClass().getMethod(method, request.getClass()).invoke(this, request);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new MSPluginException(e.getTargetException());
|
||||
} catch (Exception e) {
|
||||
throw new MSPluginException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -379,4 +379,5 @@ api_scenario_exist=场景已存在
|
|||
schedule_not_exist=定时任务不存在
|
||||
api_case_report_not_exist=用例报告不存在
|
||||
api_scenario_report_not_exist=场景报告不存在
|
||||
permission.api_plugin.name=接口插件
|
||||
|
||||
|
|
|
@ -390,4 +390,5 @@ api_swagger_url_error=Swagger url unable to connect
|
|||
api_scenario_exist=The scenario already exists
|
||||
schedule_not_exist=The scheduled task does not exist
|
||||
api_case_report_not_exist=Api report does not exist
|
||||
api_scenario_report_not_exist=Scenario report does not exist
|
||||
api_scenario_report_not_exist=Scenario report does not exist
|
||||
permission.api_plugin.name=Api Plugin
|
|
@ -360,3 +360,4 @@ api_scenario_exist=场景已存在
|
|||
schedule_not_exist=定时任务不存在
|
||||
api_case_report_not_exist=用例报告不存在
|
||||
api_scenario_report_not_exist=场景报告不存在
|
||||
permission.api_plugin.name=接口插件
|
|
@ -359,4 +359,5 @@ api_swagger_url_error=Swagger url無法調解
|
|||
api_scenario_exist=場景已存在
|
||||
schedule_not_exist=定時任務不存在
|
||||
api_case_report_not_exist=用例報告不存在
|
||||
api_scenario_report_not_exist=場景報告不存在
|
||||
api_scenario_report_not_exist=場景報告不存在
|
||||
permission.api_plugin.name=接口插件
|
|
@ -1,14 +1,17 @@
|
|||
package io.metersphere.api.controller;
|
||||
|
||||
import io.metersphere.api.dto.ApiTestPluginOptionRequest;
|
||||
import io.metersphere.api.service.ApiExecuteService;
|
||||
import io.metersphere.api.service.ApiTestService;
|
||||
import io.metersphere.jmeter.mock.Mock;
|
||||
import io.metersphere.plugin.api.dto.ApiPluginSelectOption;
|
||||
import io.metersphere.project.dto.customfunction.request.CustomFunctionRunRequest;
|
||||
import io.metersphere.sdk.constants.PermissionConstants;
|
||||
import io.metersphere.system.dto.ProtocolDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.shiro.authz.annotation.Logical;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
@ -48,4 +51,16 @@ public class ApiTestController {
|
|||
public String run(@Validated @RequestBody CustomFunctionRunRequest runRequest) {
|
||||
return apiExecuteService.runScript(runRequest);
|
||||
}
|
||||
|
||||
@PostMapping("/plugin/form/option")
|
||||
@Operation(summary = "接口测试-获取插件表单选项")
|
||||
@RequiresPermissions(value = {
|
||||
PermissionConstants.PROJECT_API_DEFINITION_READ,
|
||||
PermissionConstants.PROJECT_API_DEFINITION_CASE_READ,
|
||||
PermissionConstants.PROJECT_API_DEBUG_READ,
|
||||
PermissionConstants.PROJECT_API_SCENARIO_READ
|
||||
}, logical = Logical.OR)
|
||||
public List<ApiPluginSelectOption> getFormOptions(@Validated @RequestBody ApiTestPluginOptionRequest request) {
|
||||
return apiTestService.getFormOptions(request);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package io.metersphere.api.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApiTestPluginOptionRequest {
|
||||
/**
|
||||
* 组织ID
|
||||
*/
|
||||
@NotBlank
|
||||
@Size(max = 50)
|
||||
private String orgId;
|
||||
|
||||
/**
|
||||
* 插件ID
|
||||
*/
|
||||
@NotBlank
|
||||
@Size(max = 50)
|
||||
private String pluginId;
|
||||
|
||||
/**
|
||||
* 需要调用的插件方法
|
||||
*/
|
||||
@NotBlank
|
||||
@Size(max = 100)
|
||||
private String optionMethod;
|
||||
|
||||
/**
|
||||
* 方法所需的查询参数
|
||||
* 例如:
|
||||
* 查询的过滤条件
|
||||
* 级联的情况,需要表单的其他参数值等
|
||||
*/
|
||||
private Object queryParam;
|
||||
}
|
|
@ -1,9 +1,17 @@
|
|||
package io.metersphere.api.service;
|
||||
|
||||
import io.metersphere.api.dto.ApiTestPluginOptionRequest;
|
||||
import io.metersphere.api.dto.request.http.MsHTTPElement;
|
||||
import io.metersphere.plugin.api.dto.ApiPluginOptionsRequest;
|
||||
import io.metersphere.plugin.api.dto.ApiPluginSelectOption;
|
||||
import io.metersphere.plugin.api.spi.AbstractApiPlugin;
|
||||
import io.metersphere.sdk.util.BeanUtils;
|
||||
import io.metersphere.system.dto.ProtocolDTO;
|
||||
import io.metersphere.system.service.ApiPluginService;
|
||||
import io.metersphere.system.utils.ServiceUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.pf4j.PluginWrapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
|
@ -31,4 +39,18 @@ public class ApiTestService {
|
|||
return protocols;
|
||||
}
|
||||
|
||||
public List<ApiPluginSelectOption> getFormOptions(ApiTestPluginOptionRequest request) {
|
||||
// 获取组织下有权限的接口插件
|
||||
List<PluginWrapper> pluginWrappers = apiPluginService.getOrgApiPluginWrappers(request.getOrgId());
|
||||
PluginWrapper pluginWrapper = pluginWrappers.stream()
|
||||
.filter(item -> StringUtils.equals(item.getPluginId(), request.getPluginId()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (pluginWrapper == null || !(pluginWrapper.getPlugin() instanceof AbstractApiPlugin)) {
|
||||
// 插件不存在或者非接口插件,抛出异常
|
||||
ServiceUtils.checkResourceExist(null, "permission.api_plugin.name");
|
||||
}
|
||||
ApiPluginOptionsRequest optionsRequest = BeanUtils.copyBean(new ApiPluginOptionsRequest(), request);
|
||||
return ((AbstractApiPlugin) pluginWrapper.getPlugin()).getPluginOptions(optionsRequest);
|
||||
}
|
||||
}
|
|
@ -1,21 +1,37 @@
|
|||
package io.metersphere.api.controller;
|
||||
|
||||
import io.metersphere.api.dto.ApiTestPluginOptionRequest;
|
||||
import io.metersphere.api.service.BaseResourcePoolTestService;
|
||||
import io.metersphere.plugin.api.dto.ApiPluginSelectOption;
|
||||
import io.metersphere.project.constants.ScriptLanguageType;
|
||||
import io.metersphere.project.dto.customfunction.request.CustomFunctionRunRequest;
|
||||
import io.metersphere.project.dto.environment.KeyValueParam;
|
||||
import io.metersphere.sdk.constants.PermissionConstants;
|
||||
import io.metersphere.system.base.BasePluginTestService;
|
||||
import io.metersphere.system.base.BaseTest;
|
||||
import io.metersphere.system.domain.Plugin;
|
||||
import io.metersphere.system.dto.request.PluginUpdateRequest;
|
||||
import io.metersphere.system.service.PluginService;
|
||||
import io.metersphere.system.uid.IDGenerator;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import static io.metersphere.sdk.constants.InternalUserRole.ADMIN;
|
||||
import static io.metersphere.system.controller.handler.result.MsHttpResultCode.NOT_FOUND;
|
||||
|
||||
/**
|
||||
* @Author: jianxing
|
||||
* @CreateTime: 2023-11-07 17:07
|
||||
|
@ -29,9 +45,14 @@ public class ApiTestControllerTests extends BaseTest {
|
|||
protected static final String PROTOCOL_LIST = "protocol/{0}";
|
||||
protected static final String MOCK = "mock/{0}";
|
||||
protected static final String CUSTOM_FUNC_RUN = "custom/func/run";
|
||||
protected static final String PLUGIN_FORM_OPTION = "plugin/form/option";
|
||||
|
||||
@Resource
|
||||
private BaseResourcePoolTestService baseResourcePoolTestService;
|
||||
@Resource
|
||||
private PluginService pluginService;
|
||||
@Resource
|
||||
private BasePluginTestService basePluginTestService;
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return BASE_PATH;
|
||||
|
@ -75,4 +96,50 @@ public class ApiTestControllerTests extends BaseTest {
|
|||
// @@校验权限
|
||||
requestPostPermissionTest(PermissionConstants.PROJECT_CUSTOM_FUNCTION_EXECUTE, CUSTOM_FUNC_RUN, request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFormOptions() throws Exception {
|
||||
ApiTestPluginOptionRequest request = new ApiTestPluginOptionRequest();
|
||||
request.setOrgId(DEFAULT_ORGANIZATION_ID);
|
||||
request.setQueryParam(new HashMap<>());
|
||||
request.setPluginId("aaa");
|
||||
request.setOptionMethod("testGetApiPluginSelectOption");
|
||||
assertErrorCode(this.requestPost(PLUGIN_FORM_OPTION, request), NOT_FOUND);
|
||||
Plugin jiraPlugin = basePluginTestService.addJiraPlugin();
|
||||
request.setPluginId(jiraPlugin.getId());
|
||||
assertErrorCode(this.requestPost(PLUGIN_FORM_OPTION, request), NOT_FOUND);
|
||||
// @@请求成功
|
||||
Plugin plugin = addOptionTestPlugin();
|
||||
request.setPluginId(plugin.getId());
|
||||
MvcResult mvcResult = this.requestPostWithOkAndReturn(PLUGIN_FORM_OPTION, request);
|
||||
List<ApiPluginSelectOption> options = getResultDataArray(mvcResult, ApiPluginSelectOption.class);
|
||||
Assertions.assertEquals(options.size(), 1);
|
||||
|
||||
basePluginTestService.deleteJiraPlugin();
|
||||
pluginService.delete(plugin.getId());
|
||||
|
||||
// @@校验权限
|
||||
requestPostPermissionsTest(new ArrayList<>() {{
|
||||
add(PermissionConstants.PROJECT_API_DEFINITION_READ);
|
||||
add(PermissionConstants.PROJECT_API_DEFINITION_CASE_READ);
|
||||
add(PermissionConstants.PROJECT_API_DEBUG_READ);
|
||||
add(PermissionConstants.PROJECT_API_SCENARIO_READ);
|
||||
}}, PLUGIN_FORM_OPTION, request);
|
||||
}
|
||||
|
||||
public Plugin addOptionTestPlugin() throws Exception {
|
||||
PluginUpdateRequest request = new PluginUpdateRequest();
|
||||
File jarFile = new File(
|
||||
this.getClass().getClassLoader().getResource("file/dubbo-option-test-sampler-v3.x.jar")
|
||||
.getPath()
|
||||
);
|
||||
FileInputStream inputStream = new FileInputStream(jarFile);
|
||||
MockMultipartFile mockMultipartFile = new MockMultipartFile(jarFile.getName(), jarFile.getName(), "jar", inputStream);
|
||||
request.setName("测试获取选项插件");
|
||||
request.setGlobal(true);
|
||||
request.setEnable(true);
|
||||
request.setCreateUser(ADMIN.name());
|
||||
return pluginService.add(request, mockMultipartFile);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ public class ApiTestServiceTest extends BaseTest {
|
|||
httpProtocol.setPolymorphicName(MsHTTPElement.class.getSimpleName());
|
||||
ProtocolDTO jdbcProtocol = new ProtocolDTO();
|
||||
jdbcProtocol.setProtocol("JDBC");
|
||||
jdbcProtocol.setPluginId("jdbc");
|
||||
jdbcProtocol.setPolymorphicName("MsJDBCElement");
|
||||
expected.add(httpProtocol);
|
||||
expected.add(jdbcProtocol);
|
||||
|
|
Binary file not shown.
|
@ -17,4 +17,8 @@ public class ProtocolDTO {
|
|||
* 例如 Http 对应 MsHTTPElement
|
||||
*/
|
||||
private String polymorphicName;
|
||||
/**
|
||||
* 插件ID
|
||||
*/
|
||||
private String pluginId;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.metersphere.system.service;
|
||||
|
||||
import io.metersphere.plugin.api.spi.AbstractApiPlugin;
|
||||
import io.metersphere.plugin.api.spi.AbstractProtocolPlugin;
|
||||
import io.metersphere.plugin.api.spi.MsTestElement;
|
||||
import io.metersphere.sdk.constants.PluginScenarioType;
|
||||
|
@ -29,20 +30,12 @@ public class ApiPluginService {
|
|||
|
||||
/**
|
||||
* 获取协议插件的的协议列表
|
||||
*
|
||||
* @param orgId
|
||||
* @return
|
||||
*/
|
||||
public List<ProtocolDTO> getProtocols(String orgId) {
|
||||
// 查询组织下有权限的插件
|
||||
Set<String> pluginIds = basePluginService.getOrgEnabledPlugins(orgId, PluginScenarioType.API_PROTOCOL)
|
||||
.stream()
|
||||
.map(Plugin::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 过滤协议插件
|
||||
List<PluginWrapper> plugins = pluginLoadService.getMsPluginManager().getPlugins();
|
||||
List<PluginWrapper> pluginWrappers = plugins.stream()
|
||||
.filter(plugin -> pluginIds.contains(plugin.getPluginId()) && plugin.getPlugin() instanceof AbstractProtocolPlugin).toList();
|
||||
List<PluginWrapper> pluginWrappers = getOrgProtocolPluginWrappers(orgId);
|
||||
|
||||
List protocols = new ArrayList<ProtocolDTO>();
|
||||
pluginWrappers.forEach(pluginWrapper -> {
|
||||
|
@ -54,6 +47,7 @@ public class ApiPluginService {
|
|||
AbstractProtocolPlugin protocolPlugin = ((AbstractProtocolPlugin) pluginWrapper.getPlugin());
|
||||
ProtocolDTO protocolDTO = new ProtocolDTO();
|
||||
protocolDTO.setProtocol(protocolPlugin.getProtocol());
|
||||
protocolDTO.setPluginId(pluginWrapper.getPluginId());
|
||||
if (CollectionUtils.isNotEmpty(extensionClasses)) {
|
||||
protocolDTO.setPolymorphicName(extensionClasses.get(0).getSimpleName());
|
||||
}
|
||||
|
@ -66,4 +60,25 @@ public class ApiPluginService {
|
|||
});
|
||||
return protocols;
|
||||
}
|
||||
|
||||
private List<PluginWrapper> getOrgProtocolPluginWrappers(String orgId) {
|
||||
return getOrgApiPluginWrappers(orgId).stream()
|
||||
.filter(plugin -> plugin.getPlugin() instanceof AbstractProtocolPlugin)
|
||||
.toList();
|
||||
}
|
||||
|
||||
public List<PluginWrapper> getOrgApiPluginWrappers(String orgId) {
|
||||
// 查询组织下有权限的插件
|
||||
Set<String> pluginIds = basePluginService.getOrgEnabledPlugins(orgId, PluginScenarioType.API_PROTOCOL)
|
||||
.stream()
|
||||
.map(Plugin::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 过滤协议插件
|
||||
List<PluginWrapper> plugins = pluginLoadService.getMsPluginManager().getPlugins();
|
||||
List<PluginWrapper> pluginWrappers = plugins.stream()
|
||||
.filter(plugin -> pluginIds.contains(plugin.getPluginId()) && plugin.getPlugin() instanceof AbstractApiPlugin)
|
||||
.toList();
|
||||
return pluginWrappers;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ public class ApiPluginServiceTests extends BaseTest {
|
|||
ProtocolDTO jdbcProtocol = new ProtocolDTO();
|
||||
jdbcProtocol.setProtocol("JDBC");
|
||||
jdbcProtocol.setPolymorphicName("MsJDBCElement");
|
||||
jdbcProtocol.setPluginId("jdbc");
|
||||
expected.add(jdbcProtocol);
|
||||
Assertions.assertEquals(protocols, expected);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue