feat(接口定义): Swagger URL 导入或定时同步接口时支持添加认证
--bug=1007799 --user=周骏弘 [Github#7602]当Swagger URL存在登录验证或其它认证机制时,通过Swagger URL的方式导入API接口,报错解析失败,请优化 #7602 https://www.tapd.cn/55049933/s/1079026
This commit is contained in:
parent
afb0328a8b
commit
43eb75c26f
|
@ -1,7 +1,10 @@
|
|||
package io.metersphere.api.dto;
|
||||
|
||||
import io.metersphere.api.dto.definition.request.auth.MsAuthManager;
|
||||
import io.metersphere.api.dto.scenario.KeyValue;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import java.util.List;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
|
@ -24,4 +27,9 @@ public class ApiTestImportRequest {
|
|||
private String type;
|
||||
// 是否开启自定义ID
|
||||
private Boolean openCustomNum = false;
|
||||
// 鉴权相关
|
||||
private List<KeyValue> headers;
|
||||
private List<KeyValue> arguments;
|
||||
private MsAuthManager authManager;
|
||||
|
||||
}
|
||||
|
|
|
@ -14,13 +14,13 @@ import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
|
|||
import io.metersphere.base.domain.ApiModule;
|
||||
import io.metersphere.commons.constants.SwaggerParameterType;
|
||||
import io.swagger.models.*;
|
||||
import io.swagger.models.auth.AuthorizationValue;
|
||||
import io.swagger.models.parameters.*;
|
||||
import io.swagger.models.properties.*;
|
||||
import io.swagger.parser.SwaggerParser;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
|
||||
|
@ -32,24 +32,69 @@ public class Swagger2Parser extends SwaggerAbstractParser {
|
|||
public ApiDefinitionImport parse(InputStream source, ApiTestImportRequest request) {
|
||||
Swagger swagger;
|
||||
String sourceStr = "";
|
||||
List<AuthorizationValue> auths = setAuths(request);
|
||||
if (StringUtils.isNotBlank(request.getSwaggerUrl())) { // 使用 url 导入 swagger
|
||||
swagger = new SwaggerParser().read(request.getSwaggerUrl());
|
||||
swagger = new SwaggerParser().read(request.getSwaggerUrl(), auths, true);
|
||||
} else {
|
||||
sourceStr = getApiTestStr(source); // 导入的二进制文件转换为 String
|
||||
swagger = new SwaggerParser().readWithInfo(sourceStr).getSwagger();
|
||||
swagger = new SwaggerParser().readWithInfo(sourceStr, auths, true).getSwagger();
|
||||
}
|
||||
|
||||
if (swagger == null || swagger.getSwagger() == null) { // 不是 2.0 版本,则尝试转换 3.0
|
||||
Swagger3Parser swagger3Parser = new Swagger3Parser();
|
||||
|
||||
return swagger3Parser.parse(sourceStr, request);
|
||||
}
|
||||
|
||||
ApiDefinitionImport definitionImport = new ApiDefinitionImport();
|
||||
this.projectId = request.getProjectId();
|
||||
definitionImport.setData(parseRequests(swagger, request));
|
||||
return definitionImport;
|
||||
}
|
||||
|
||||
// 鉴权设置
|
||||
private List<AuthorizationValue> setAuths(ApiTestImportRequest request) {
|
||||
List<AuthorizationValue> auths = new ArrayList<>();
|
||||
// 如果有 BaseAuth 参数,base64 编码后转换成 headers
|
||||
if(request.getAuthManager() != null
|
||||
&& StringUtils.isNotBlank(request.getAuthManager().getUsername())
|
||||
&& StringUtils.isNotBlank(request.getAuthManager().getPassword())
|
||||
&& request.getAuthManager().getVerification().equals("Basic Auth")){
|
||||
AuthorizationValue authorizationValue = new AuthorizationValue();
|
||||
authorizationValue.setType("header");
|
||||
authorizationValue.setKeyName("Authorization");
|
||||
String authValue = "Basic " + Base64.getUrlEncoder().encodeToString((request.getAuthManager().getUsername()
|
||||
+ ":" + request.getAuthManager().getPassword()).getBytes());
|
||||
authorizationValue.setValue(authValue);
|
||||
auths.add(authorizationValue);
|
||||
}
|
||||
// 设置 headers
|
||||
if(!CollectionUtils.isEmpty(request.getHeaders())){
|
||||
for(KeyValue keyValue : request.getHeaders()){
|
||||
// 当有 key 时才进行设置
|
||||
if(StringUtils.isNotBlank(keyValue.getName())){
|
||||
AuthorizationValue authorizationValue = new AuthorizationValue();
|
||||
authorizationValue.setType("header");
|
||||
authorizationValue.setKeyName(keyValue.getName());
|
||||
authorizationValue.setValue(keyValue.getValue());
|
||||
auths.add(authorizationValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 设置 query 参数
|
||||
if(!CollectionUtils.isEmpty(request.getArguments())){
|
||||
for(KeyValue keyValue : request.getArguments()){
|
||||
if(StringUtils.isNotBlank(keyValue.getName())){
|
||||
AuthorizationValue authorizationValue = new AuthorizationValue();
|
||||
authorizationValue.setType("query");
|
||||
authorizationValue.setKeyName(keyValue.getName());
|
||||
authorizationValue.setValue(keyValue.getValue());
|
||||
auths.add(authorizationValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
return CollectionUtils.size(auths) == 0 ? null : auths;
|
||||
}
|
||||
|
||||
|
||||
private List<ApiDefinitionWithBLOBs> parseRequests(Swagger swagger, ApiTestImportRequest importRequest) {
|
||||
Map<String, Path> paths = swagger.getPaths();
|
||||
Set<String> pathNames = paths.keySet();
|
||||
|
|
|
@ -27,12 +27,12 @@ import io.swagger.v3.oas.models.media.*;
|
|||
import io.swagger.v3.oas.models.parameters.*;
|
||||
import io.swagger.v3.oas.models.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.models.responses.ApiResponses;
|
||||
import io.swagger.v3.parser.core.models.AuthorizationValue;
|
||||
import io.swagger.v3.parser.core.models.SwaggerParseResult;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
|
||||
|
@ -51,29 +51,74 @@ public class Swagger3Parser extends SwaggerAbstractParser {
|
|||
}
|
||||
|
||||
public ApiDefinitionImport parse(String sourceStr, ApiTestImportRequest request) {
|
||||
|
||||
List<AuthorizationValue> auths = setAuths(request);
|
||||
SwaggerParseResult result;
|
||||
if (StringUtils.isNotBlank(request.getSwaggerUrl())) {
|
||||
result = new OpenAPIParser().readLocation(request.getSwaggerUrl(), null, null);
|
||||
result = new OpenAPIParser().readLocation(request.getSwaggerUrl(), auths, null);
|
||||
} else {
|
||||
result = new OpenAPIParser().readContents(sourceStr, null, null);
|
||||
result = new OpenAPIParser().readContents(sourceStr, auths, null);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
MSException.throwException("解析失败,请确认选择的是 swagger 格式!");
|
||||
}
|
||||
|
||||
OpenAPI openAPI = result.getOpenAPI();
|
||||
|
||||
if (result.getMessages() != null) {
|
||||
result.getMessages().forEach(msg -> LogUtil.error(msg)); // validation errors and warnings
|
||||
}
|
||||
|
||||
ApiDefinitionImport definitionImport = new ApiDefinitionImport();
|
||||
this.projectId = request.getProjectId();
|
||||
definitionImport.setData(parseRequests(openAPI, request));
|
||||
return definitionImport;
|
||||
}
|
||||
|
||||
|
||||
// 鉴权设置
|
||||
private List<AuthorizationValue> setAuths(ApiTestImportRequest request) {
|
||||
List<AuthorizationValue> auths = new ArrayList<>();
|
||||
// 如果有 BaseAuth 参数,base64 编码后转换成 headers
|
||||
if(request.getAuthManager() != null
|
||||
&& StringUtils.isNotBlank(request.getAuthManager().getUsername())
|
||||
&& StringUtils.isNotBlank(request.getAuthManager().getPassword())
|
||||
&& request.getAuthManager().getVerification().equals("Basic Auth")){
|
||||
AuthorizationValue authorizationValue = new AuthorizationValue();
|
||||
authorizationValue.setType("header");
|
||||
authorizationValue.setKeyName("Authorization");
|
||||
String authValue = "Basic " + Base64.getUrlEncoder().encodeToString((request.getAuthManager().getUsername()
|
||||
+ ":" + request.getAuthManager().getPassword()).getBytes());
|
||||
authorizationValue.setValue(authValue);
|
||||
auths.add(authorizationValue);
|
||||
}
|
||||
// 设置 headers
|
||||
if(!CollectionUtils.isEmpty(request.getHeaders())){
|
||||
for(KeyValue keyValue : request.getHeaders()){
|
||||
// 当有 key 时才进行设置
|
||||
if(StringUtils.isNotBlank(keyValue.getName())){
|
||||
AuthorizationValue authorizationValue = new AuthorizationValue();
|
||||
authorizationValue.setType("header");
|
||||
authorizationValue.setKeyName(keyValue.getName());
|
||||
authorizationValue.setValue(keyValue.getValue());
|
||||
auths.add(authorizationValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 设置 query 参数
|
||||
if(!CollectionUtils.isEmpty(request.getArguments())){
|
||||
for(KeyValue keyValue : request.getArguments()){
|
||||
if(StringUtils.isNotBlank(keyValue.getName())){
|
||||
AuthorizationValue authorizationValue = new AuthorizationValue();
|
||||
authorizationValue.setType("query");
|
||||
authorizationValue.setKeyName(keyValue.getName());
|
||||
authorizationValue.setValue(keyValue.getValue());
|
||||
auths.add(authorizationValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
return CollectionUtils.size(auths) == 0 ? null : auths;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private List<ApiDefinitionWithBLOBs> parseRequests(OpenAPI openAPI, ApiTestImportRequest importRequest) {
|
||||
Paths paths = openAPI.getPaths();
|
||||
|
||||
|
|
|
@ -66,7 +66,6 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import sun.security.util.Cache;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.*;
|
||||
|
@ -1351,10 +1350,13 @@ public class ApiDefinitionService {
|
|||
|
||||
/*swagger定时导入*/
|
||||
public void createSchedule(ScheduleRequest request) {
|
||||
String config = setAuthParams(request);
|
||||
/*保存swaggerUrl*/
|
||||
SwaggerUrlProject swaggerUrlProject = new SwaggerUrlProject();
|
||||
BeanUtils.copyBean(swaggerUrlProject, request);
|
||||
swaggerUrlProject.setId(UUID.randomUUID().toString());
|
||||
// 设置鉴权信息
|
||||
swaggerUrlProject.setConfig(config);
|
||||
scheduleService.addSwaggerUrlSchedule(swaggerUrlProject);
|
||||
|
||||
request.setResourceId(swaggerUrlProject.getId());
|
||||
|
@ -1375,8 +1377,11 @@ public class ApiDefinitionService {
|
|||
}
|
||||
|
||||
public void updateSchedule(ScheduleRequest request) {
|
||||
String config = setAuthParams(request);
|
||||
SwaggerUrlProject swaggerUrlProject = new SwaggerUrlProject();
|
||||
BeanUtils.copyBean(swaggerUrlProject, request);
|
||||
// 设置鉴权信息
|
||||
swaggerUrlProject.setConfig(config);
|
||||
scheduleService.updateSwaggerUrlSchedule(swaggerUrlProject);
|
||||
// 只修改表达式和名称
|
||||
Schedule schedule = new Schedule();
|
||||
|
@ -1394,6 +1399,24 @@ public class ApiDefinitionService {
|
|||
this.addOrUpdateSwaggerImportCronJob(request);
|
||||
}
|
||||
|
||||
|
||||
// 设置 SwaggerUrl 同步鉴权参数
|
||||
public String setAuthParams(ScheduleRequest request){
|
||||
// list 数组转化成 json 字符串
|
||||
JSONObject configObj = new JSONObject();
|
||||
configObj.put("headers", request.getHeaders());
|
||||
configObj.put("arguments", request.getArguments());
|
||||
// 设置 BaseAuth 参数
|
||||
if(request.getAuthManager() != null
|
||||
&& StringUtils.isNotBlank(request.getAuthManager().getUsername())
|
||||
&& StringUtils.isNotBlank(request.getAuthManager().getPassword())){
|
||||
configObj.put("authManager", request.getAuthManager());
|
||||
}
|
||||
return JSONObject.toJSONString(configObj);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 列表开关切换
|
||||
*
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package io.metersphere.base.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SwaggerUrlProject implements Serializable {
|
||||
|
@ -18,5 +17,7 @@ public class SwaggerUrlProject implements Serializable {
|
|||
|
||||
private String modeId;
|
||||
|
||||
private String config;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
|
@ -2,9 +2,8 @@ package io.metersphere.base.mapper;
|
|||
|
||||
import io.metersphere.base.domain.SwaggerUrlProject;
|
||||
import io.metersphere.base.domain.SwaggerUrlProjectExample;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
public interface SwaggerUrlProjectMapper {
|
||||
long countByExample(SwaggerUrlProjectExample example);
|
||||
|
@ -17,15 +16,21 @@ public interface SwaggerUrlProjectMapper {
|
|||
|
||||
int insertSelective(SwaggerUrlProject record);
|
||||
|
||||
List<SwaggerUrlProject> selectByExampleWithBLOBs(SwaggerUrlProjectExample example);
|
||||
|
||||
List<SwaggerUrlProject> selectByExample(SwaggerUrlProjectExample example);
|
||||
|
||||
SwaggerUrlProject selectByPrimaryKey(String id);
|
||||
|
||||
int updateByExampleSelective(@Param("record") SwaggerUrlProject record, @Param("example") SwaggerUrlProjectExample example);
|
||||
|
||||
int updateByExampleWithBLOBs(@Param("record") SwaggerUrlProject record, @Param("example") SwaggerUrlProjectExample example);
|
||||
|
||||
int updateByExample(@Param("record") SwaggerUrlProject record, @Param("example") SwaggerUrlProjectExample example);
|
||||
|
||||
int updateByPrimaryKeySelective(SwaggerUrlProject record);
|
||||
|
||||
int updateByPrimaryKeyWithBLOBs(SwaggerUrlProject record);
|
||||
|
||||
int updateByPrimaryKey(SwaggerUrlProject record);
|
||||
}
|
|
@ -2,12 +2,15 @@
|
|||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="io.metersphere.base.mapper.SwaggerUrlProjectMapper">
|
||||
<resultMap id="BaseResultMap" type="io.metersphere.base.domain.SwaggerUrlProject">
|
||||
<id column="id" jdbcType="VARCHAR" property="id"/>
|
||||
<result column="project_id" jdbcType="VARCHAR" property="projectId"/>
|
||||
<result column="swagger_url" jdbcType="VARCHAR" property="swaggerUrl"/>
|
||||
<result column="module_id" jdbcType="VARCHAR" property="moduleId"/>
|
||||
<result column="module_path" jdbcType="VARCHAR" property="modulePath"/>
|
||||
<result column="mode_id" jdbcType="VARCHAR" property="modeId"/>
|
||||
<id column="id" jdbcType="VARCHAR" property="id" />
|
||||
<result column="project_id" jdbcType="VARCHAR" property="projectId" />
|
||||
<result column="swagger_url" jdbcType="VARCHAR" property="swaggerUrl" />
|
||||
<result column="module_id" jdbcType="VARCHAR" property="moduleId" />
|
||||
<result column="module_path" jdbcType="VARCHAR" property="modulePath" />
|
||||
<result column="mode_id" jdbcType="VARCHAR" property="modeId" />
|
||||
</resultMap>
|
||||
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.base.domain.SwaggerUrlProject">
|
||||
<result column="config" jdbcType="LONGVARCHAR" property="config" />
|
||||
</resultMap>
|
||||
<sql id="Example_Where_Clause">
|
||||
<where>
|
||||
|
@ -70,43 +73,64 @@
|
|||
<sql id="Base_Column_List">
|
||||
id, project_id, swagger_url, module_id, module_path, mode_id
|
||||
</sql>
|
||||
<select id="selectByExample" parameterType="io.metersphere.base.domain.SwaggerUrlProjectExample"
|
||||
resultMap="BaseResultMap">
|
||||
<sql id="Blob_Column_List">
|
||||
config
|
||||
</sql>
|
||||
<select id="selectByExampleWithBLOBs" parameterType="io.metersphere.base.domain.SwaggerUrlProjectExample" resultMap="ResultMapWithBLOBs">
|
||||
select
|
||||
<if test="distinct">
|
||||
distinct
|
||||
</if>
|
||||
<include refid="Base_Column_List"/>
|
||||
<include refid="Base_Column_List" />
|
||||
,
|
||||
<include refid="Blob_Column_List" />
|
||||
from swagger_url_project
|
||||
<if test="_parameter != null">
|
||||
<include refid="Example_Where_Clause"/>
|
||||
<include refid="Example_Where_Clause" />
|
||||
</if>
|
||||
<if test="orderByClause != null">
|
||||
order by ${orderByClause}
|
||||
</if>
|
||||
</select>
|
||||
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
|
||||
<select id="selectByExample" parameterType="io.metersphere.base.domain.SwaggerUrlProjectExample" resultMap="BaseResultMap">
|
||||
select
|
||||
<include refid="Base_Column_List"/>
|
||||
<if test="distinct">
|
||||
distinct
|
||||
</if>
|
||||
<include refid="Base_Column_List" />
|
||||
from swagger_url_project
|
||||
<if test="_parameter != null">
|
||||
<include refid="Example_Where_Clause" />
|
||||
</if>
|
||||
<if test="orderByClause != null">
|
||||
order by ${orderByClause}
|
||||
</if>
|
||||
</select>
|
||||
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="ResultMapWithBLOBs">
|
||||
select
|
||||
<include refid="Base_Column_List" />
|
||||
,
|
||||
<include refid="Blob_Column_List" />
|
||||
from swagger_url_project
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</select>
|
||||
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
|
||||
delete
|
||||
from swagger_url_project
|
||||
delete from swagger_url_project
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</delete>
|
||||
<delete id="deleteByExample" parameterType="io.metersphere.base.domain.SwaggerUrlProjectExample">
|
||||
delete from swagger_url_project
|
||||
<if test="_parameter != null">
|
||||
<include refid="Example_Where_Clause"/>
|
||||
<include refid="Example_Where_Clause" />
|
||||
</if>
|
||||
</delete>
|
||||
<insert id="insert" parameterType="io.metersphere.base.domain.SwaggerUrlProject">
|
||||
insert into swagger_url_project (id, project_id, swagger_url,
|
||||
module_id, module_path, mode_id)
|
||||
values (#{id,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{swaggerUrl,jdbcType=VARCHAR},
|
||||
#{moduleId,jdbcType=VARCHAR}, #{modulePath,jdbcType=VARCHAR}, #{modeId,jdbcType=VARCHAR})
|
||||
insert into swagger_url_project (id, project_id, swagger_url,
|
||||
module_id, module_path, mode_id,
|
||||
config)
|
||||
values (#{id,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{swaggerUrl,jdbcType=VARCHAR},
|
||||
#{moduleId,jdbcType=VARCHAR}, #{modulePath,jdbcType=VARCHAR}, #{modeId,jdbcType=VARCHAR},
|
||||
#{config,jdbcType=LONGVARCHAR})
|
||||
</insert>
|
||||
<insert id="insertSelective" parameterType="io.metersphere.base.domain.SwaggerUrlProject">
|
||||
insert into swagger_url_project
|
||||
|
@ -129,6 +153,9 @@
|
|||
<if test="modeId != null">
|
||||
mode_id,
|
||||
</if>
|
||||
<if test="config != null">
|
||||
config,
|
||||
</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="id != null">
|
||||
|
@ -149,13 +176,15 @@
|
|||
<if test="modeId != null">
|
||||
#{modeId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="config != null">
|
||||
#{config,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
</trim>
|
||||
</insert>
|
||||
<select id="countByExample" parameterType="io.metersphere.base.domain.SwaggerUrlProjectExample"
|
||||
resultType="java.lang.Long">
|
||||
<select id="countByExample" parameterType="io.metersphere.base.domain.SwaggerUrlProjectExample" resultType="java.lang.Long">
|
||||
select count(*) from swagger_url_project
|
||||
<if test="_parameter != null">
|
||||
<include refid="Example_Where_Clause"/>
|
||||
<include refid="Example_Where_Clause" />
|
||||
</if>
|
||||
</select>
|
||||
<update id="updateByExampleSelective" parameterType="map">
|
||||
|
@ -179,21 +208,37 @@
|
|||
<if test="record.modeId != null">
|
||||
mode_id = #{record.modeId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.config != null">
|
||||
config = #{record.config,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
</set>
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause"/>
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
</update>
|
||||
<update id="updateByExampleWithBLOBs" parameterType="map">
|
||||
update swagger_url_project
|
||||
set id = #{record.id,jdbcType=VARCHAR},
|
||||
project_id = #{record.projectId,jdbcType=VARCHAR},
|
||||
swagger_url = #{record.swaggerUrl,jdbcType=VARCHAR},
|
||||
module_id = #{record.moduleId,jdbcType=VARCHAR},
|
||||
module_path = #{record.modulePath,jdbcType=VARCHAR},
|
||||
mode_id = #{record.modeId,jdbcType=VARCHAR},
|
||||
config = #{record.config,jdbcType=LONGVARCHAR}
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
</update>
|
||||
<update id="updateByExample" parameterType="map">
|
||||
update swagger_url_project
|
||||
set id = #{record.id,jdbcType=VARCHAR},
|
||||
project_id = #{record.projectId,jdbcType=VARCHAR},
|
||||
swagger_url = #{record.swaggerUrl,jdbcType=VARCHAR},
|
||||
module_id = #{record.moduleId,jdbcType=VARCHAR},
|
||||
module_path = #{record.modulePath,jdbcType=VARCHAR},
|
||||
mode_id = #{record.modeId,jdbcType=VARCHAR}
|
||||
project_id = #{record.projectId,jdbcType=VARCHAR},
|
||||
swagger_url = #{record.swaggerUrl,jdbcType=VARCHAR},
|
||||
module_id = #{record.moduleId,jdbcType=VARCHAR},
|
||||
module_path = #{record.modulePath,jdbcType=VARCHAR},
|
||||
mode_id = #{record.modeId,jdbcType=VARCHAR}
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause"/>
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
</update>
|
||||
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.base.domain.SwaggerUrlProject">
|
||||
|
@ -214,16 +259,29 @@
|
|||
<if test="modeId != null">
|
||||
mode_id = #{modeId,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="config != null">
|
||||
config = #{config,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
</set>
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.base.domain.SwaggerUrlProject">
|
||||
update swagger_url_project
|
||||
set project_id = #{projectId,jdbcType=VARCHAR},
|
||||
swagger_url = #{swaggerUrl,jdbcType=VARCHAR},
|
||||
module_id = #{moduleId,jdbcType=VARCHAR},
|
||||
module_path = #{modulePath,jdbcType=VARCHAR},
|
||||
mode_id = #{modeId,jdbcType=VARCHAR},
|
||||
config = #{config,jdbcType=LONGVARCHAR}
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
<update id="updateByPrimaryKey" parameterType="io.metersphere.base.domain.SwaggerUrlProject">
|
||||
update swagger_url_project
|
||||
set project_id = #{projectId,jdbcType=VARCHAR},
|
||||
swagger_url = #{swaggerUrl,jdbcType=VARCHAR},
|
||||
module_id = #{moduleId,jdbcType=VARCHAR},
|
||||
module_path = #{modulePath,jdbcType=VARCHAR},
|
||||
mode_id = #{modeId,jdbcType=VARCHAR}
|
||||
set project_id = #{projectId,jdbcType=VARCHAR},
|
||||
swagger_url = #{swaggerUrl,jdbcType=VARCHAR},
|
||||
module_id = #{moduleId,jdbcType=VARCHAR},
|
||||
module_path = #{modulePath,jdbcType=VARCHAR},
|
||||
mode_id = #{modeId,jdbcType=VARCHAR}
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
</mapper>
|
|
@ -9,6 +9,7 @@
|
|||
sup.mode_id,
|
||||
sup.module_id,
|
||||
sup.project_id,
|
||||
sup.config,
|
||||
sch.value as rule,
|
||||
sch.enable,
|
||||
sch.id as taskId
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package io.metersphere.controller.request;
|
||||
|
||||
import io.metersphere.api.dto.definition.request.auth.MsAuthManager;
|
||||
import io.metersphere.api.dto.scenario.KeyValue;
|
||||
import io.metersphere.base.domain.Schedule;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author song.tianyang
|
||||
|
@ -28,4 +31,9 @@ public class ScheduleRequest extends Schedule {
|
|||
|
||||
private String taskId;
|
||||
|
||||
// 鉴权相关
|
||||
private List<KeyValue> headers;
|
||||
private List<KeyValue> arguments;
|
||||
private MsAuthManager authManager;
|
||||
|
||||
}
|
||||
|
|
|
@ -1,14 +1,21 @@
|
|||
package io.metersphere.job.sechedule;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.api.dto.ApiTestImportRequest;
|
||||
import io.metersphere.api.dto.definition.request.auth.MsAuthManager;
|
||||
import io.metersphere.api.dto.scenario.KeyValue;
|
||||
import io.metersphere.api.service.ApiDefinitionService;
|
||||
import io.metersphere.base.domain.SwaggerUrlProject;
|
||||
import io.metersphere.commons.constants.ScheduleGroup;
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.quartz.JobDataMap;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobKey;
|
||||
import org.quartz.TriggerKey;
|
||||
import java.util.List;
|
||||
|
||||
public class SwaggerUrlImportJob extends MsScheduleJob {
|
||||
private ApiDefinitionService apiDefinitionService;
|
||||
|
@ -21,8 +28,11 @@ public class SwaggerUrlImportJob extends MsScheduleJob {
|
|||
void businessExecute(JobExecutionContext context) {
|
||||
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
|
||||
String resourceId = jobDataMap.getString("resourceId");
|
||||
SwaggerUrlProject swaggerUrlProject=apiDefinitionService.getSwaggerInfo(resourceId);
|
||||
SwaggerUrlProject swaggerUrlProject = apiDefinitionService.getSwaggerInfo(resourceId);
|
||||
ApiTestImportRequest request = new ApiTestImportRequest();
|
||||
// 获取鉴权设置
|
||||
String config = swaggerUrlProject.getConfig();
|
||||
setAuthInfo(config, request);
|
||||
request.setProjectId(swaggerUrlProject.getProjectId());
|
||||
request.setSwaggerUrl(swaggerUrlProject.getSwaggerUrl());
|
||||
request.setModuleId(swaggerUrlProject.getModuleId());
|
||||
|
@ -41,4 +51,24 @@ public class SwaggerUrlImportJob extends MsScheduleJob {
|
|||
public static TriggerKey getTriggerKey(String resourceId) {
|
||||
return new TriggerKey(resourceId, ScheduleGroup.SWAGGER_IMPORT.name());
|
||||
}
|
||||
|
||||
public void setAuthInfo(String config, ApiTestImportRequest request){
|
||||
// 获取鉴权设置
|
||||
if(StringUtils.isNotBlank(config)){
|
||||
JSONObject configObj = JSON.parseObject(config);
|
||||
List<KeyValue> headers = JSONObject.parseArray(configObj.getString("headers"), KeyValue.class);
|
||||
if(CollectionUtils.isNotEmpty(headers)){
|
||||
request.setHeaders(headers);
|
||||
}
|
||||
List<KeyValue> arguments = JSONObject.parseArray(configObj.getString("arguments"), KeyValue.class);
|
||||
if(CollectionUtils.isNotEmpty(arguments)){
|
||||
request.setArguments(arguments);
|
||||
}
|
||||
MsAuthManager msAuthManager = JSONObject.parseObject(configObj.getString("authManager"), MsAuthManager.class);
|
||||
if(msAuthManager != null){
|
||||
request.setAuthManager(msAuthManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
-- 新增字段
|
||||
ALTER TABLE `swagger_url_project` ADD COLUMN `config` longtext COMMENT '鉴权配置信息' AFTER `mode_id`;
|
|
@ -34,7 +34,7 @@
|
|||
</el-tab-pane>
|
||||
|
||||
<!--加密-->
|
||||
<el-tab-pane :label="$t('api_test.definition.request.encryption')" name="encryption">
|
||||
<el-tab-pane :label="$t('api_test.definition.request.encryption')" name="encryption" v-if="encryptShow">
|
||||
<el-form :model="authConfig" size="small" :rules="rule"
|
||||
ref="authConfig">
|
||||
|
||||
|
@ -62,19 +62,18 @@ export default {
|
|||
components: {},
|
||||
props: {
|
||||
request: {},
|
||||
encryptShow: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
request() {
|
||||
this.initData();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.request.hashTree) {
|
||||
for (let index in this.request.hashTree) {
|
||||
if (this.request.hashTree[index].type == 'AuthManager') {
|
||||
this.request.authManager = this.request.hashTree[index];
|
||||
this.request.hashTree.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.request.authManager) {
|
||||
this.authConfig = this.request.authManager;
|
||||
}
|
||||
this.initData();
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -91,8 +90,14 @@ export default {
|
|||
let authManager = createComponent("AuthManager");
|
||||
authManager.verification = "Basic Auth";
|
||||
authManager.environment = this.request.useEnvironment;
|
||||
if(this.request.hashTree == undefined){
|
||||
this.request.hashTree = [];
|
||||
}
|
||||
this.request.hashTree.push(authManager);
|
||||
this.authConfig = authManager;
|
||||
// 这里做个判断,如果原来有值则不覆盖
|
||||
if(this.authConfig.username == undefined && this.authConfig.password == undefined){
|
||||
this.authConfig = authManager;
|
||||
}
|
||||
} else {
|
||||
for (let index in this.request.hashTree) {
|
||||
if (this.request.hashTree[index].type === "AuthManager") {
|
||||
|
@ -102,6 +107,19 @@ export default {
|
|||
this.request.authManager = {};
|
||||
}
|
||||
this.request.authManager = this.authConfig;
|
||||
},
|
||||
initData(){
|
||||
if (this.request.hashTree) {
|
||||
for (let index in this.request.hashTree) {
|
||||
if (this.request.hashTree[index].type == 'AuthManager') {
|
||||
this.request.authManager = this.request.hashTree[index];
|
||||
this.request.hashTree.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.request.authManager) {
|
||||
this.authConfig = this.request.authManager;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -144,7 +144,7 @@
|
|||
components: {
|
||||
ApiOtherInfo,
|
||||
MsFormDivider,
|
||||
MsJsr233Processor, MsResponseText, MsApiRequestForm, MsInputTag, MsSelectTree,MsChangeHistory},
|
||||
MsJsr233Processor, MsResponseText, MsApiRequestForm, MsInputTag, MsSelectTree, MsChangeHistory},
|
||||
data() {
|
||||
let validateURL = (rule, value, callback) => {
|
||||
if (!this.httpForm.path.startsWith("/") || this.httpForm.path.match(/\s/) != null) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<el-dialog :close-on-click-modal="false" :title="$t('api_test.api_import.title')" width="30%"
|
||||
<el-dialog :close-on-click-modal="false" :title="$t('api_test.api_import.title')" width="90%"
|
||||
:visible.sync="visible" class="api-import" v-loading="result.loading" @close="close"
|
||||
:destroy-on-close="true">
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
|||
|
||||
<el-form :model="formData" :rules="rules" label-width="100px" v-loading="result.loading" ref="form">
|
||||
<el-row>
|
||||
<el-col :span="11">
|
||||
<el-col :span="8">
|
||||
<el-form-item :label="$t('commons.import_module')" prop="moduleId">
|
||||
<ms-select-tree size="small" :data="moduleOptions" :defaultKey="formData.moduleId" @getValue="setModule" :obj="moduleObj" clearable checkStrictly/>
|
||||
</el-form-item>
|
||||
|
@ -49,11 +49,32 @@
|
|||
<el-col :span="1">
|
||||
<el-divider direction="vertical"/>
|
||||
</el-col>
|
||||
<el-col :span="12" v-show="isSwagger2 && swaggerUrlEnable" style="margin-top: 40px">
|
||||
|
||||
<el-col :span="14" v-show="isSwagger2 && swaggerUrlEnable" style="margin-top: 40px">
|
||||
<el-form-item :label="'Swagger URL'" prop="swaggerUrl" class="swagger-url">
|
||||
<el-input size="small" v-model="formData.swaggerUrl" clearable show-word-limit/>
|
||||
</el-form-item>
|
||||
<el-switch v-model="authEnable" :active-text="$t('api_test.api_import.add_request_params')"></el-switch>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="14" v-show="isSwagger2 && authEnable && swaggerUrlEnable">
|
||||
<!--请求头 -->
|
||||
<div style="margin-top: 15px;">
|
||||
<span>{{$t('api_test.request.headers')}}{{$t('api_test.api_import.optional')}}:</span>
|
||||
</div>
|
||||
<ms-api-key-value :show-desc="true" :isShowEnable="isShowEnable" :suggestions="headerSuggestions" :items="headers"/>
|
||||
<!--query 参数-->
|
||||
<div style="margin-top: 10px">
|
||||
<span>{{$t('api_test.definition.request.query_param')}}{{$t('api_test.api_import.optional')}}:</span>
|
||||
</div>
|
||||
<ms-api-variable :with-mor-setting="true" :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :parameters="queryArguments"/>
|
||||
<!--认证配置-->
|
||||
<div style="margin-top: 10px">
|
||||
<span>{{$t('api_test.definition.request.auth_config')}}{{$t('api_test.api_import.optional')}}:</span>
|
||||
</div>
|
||||
<ms-api-auth-config :is-read-only="isReadOnly" :request="authConfig" :encryptShow="false"/>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12"
|
||||
v-if="selectedPlatformValue != 'Swagger2' || (selectedPlatformValue == 'Swagger2' && !swaggerUrlEnable)">
|
||||
<el-upload
|
||||
|
@ -98,10 +119,20 @@
|
|||
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
|
||||
import {listenGoBack, removeGoBackListener, hasLicense, getCurrentProjectID} from "@/common/js/utils";
|
||||
import MsSelectTree from "../../../../common/select-tree/SelectTree";
|
||||
import MsApiKeyValue from "../ApiKeyValue";
|
||||
import MsApiVariable from "../ApiVariable";
|
||||
import MsApiAuthConfig from "../auth/ApiAuthConfig";
|
||||
import {REQUEST_HEADERS} from "@/common/js/constants";
|
||||
import {ELEMENT_TYPE, TYPE_TO_C} from "@/business/components/api/automation/scenario/Setting";
|
||||
|
||||
export default {
|
||||
name: "ApiImport",
|
||||
components: {MsDialogFooter, MsSelectTree},
|
||||
components: {
|
||||
MsDialogFooter,
|
||||
MsSelectTree,
|
||||
MsApiKeyValue,
|
||||
MsApiVariable,
|
||||
MsApiAuthConfig},
|
||||
props: {
|
||||
saved: {
|
||||
type: Boolean,
|
||||
|
@ -118,8 +149,10 @@
|
|||
return {
|
||||
visible: false,
|
||||
swaggerUrlEnable: false,
|
||||
authEnable: false,
|
||||
showEnvironmentSelect: true,
|
||||
showXpackCompnent:false,
|
||||
headerSuggestions: REQUEST_HEADERS,
|
||||
moduleObj: {
|
||||
id: 'id',
|
||||
label: 'name',
|
||||
|
@ -189,7 +222,7 @@
|
|||
file: undefined,
|
||||
swaggerUrl: '',
|
||||
modeId: 'incrementalMerge',
|
||||
moduleId: '',
|
||||
moduleId: ''
|
||||
},
|
||||
rules: {
|
||||
modeId: [
|
||||
|
@ -197,7 +230,14 @@
|
|||
],
|
||||
},
|
||||
currentModule: {},
|
||||
fileList: []
|
||||
fileList: [],
|
||||
isShowEnable: true,
|
||||
isReadOnly: false,
|
||||
headers: [],
|
||||
queryArguments: [],
|
||||
authConfig: {
|
||||
hashTree: []
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
@ -341,6 +381,14 @@
|
|||
param.projectId = this.projectId;
|
||||
if (!this.swaggerUrlEnable) {
|
||||
param.swaggerUrl = undefined;
|
||||
}else{
|
||||
// 设置请求头
|
||||
param.headers = this.headers;
|
||||
// 设置 query 参数
|
||||
param.arguments = this.queryArguments;
|
||||
// 设置 BaseAuth 参数
|
||||
this.authConfig.authManager.clazzName = TYPE_TO_C.get("AuthManager");
|
||||
param.authManager = this.authConfig.authManager;
|
||||
}
|
||||
return param;
|
||||
},
|
||||
|
|
|
@ -30,6 +30,29 @@
|
|||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" style="margin-left: 50px">
|
||||
<el-switch v-model="authEnable" :active-text="$t('api_test.api_import.add_request_params')"></el-switch>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="19" v-show="authEnable" style="margin-top: 10px; margin-left: 50px" class="request-tabs">
|
||||
<!-- 请求头 -->
|
||||
<div>
|
||||
<span>{{$t('api_test.request.headers')}}{{$t('api_test.api_import.optional')}}:</span>
|
||||
</div>
|
||||
<ms-api-key-value :label="$t('api_test.definition.request.auth_config')"
|
||||
:show-desc="true" :isShowEnable="isShowEnable" :suggestions="headerSuggestions" :items="headers"/>
|
||||
<!--query 参数-->
|
||||
<div style="margin-top: 10px">
|
||||
<span>{{$t('api_test.definition.request.query_param')}}{{$t('api_test.api_import.optional')}}:</span>
|
||||
</div>
|
||||
<ms-api-variable :with-mor-setting="true" :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :parameters="queryArguments"/>
|
||||
<!--认证配置-->
|
||||
<div style="margin-top: 10px">
|
||||
<span>{{$t('api_test.definition.request.auth_config')}}{{$t('api_test.api_import.optional')}}:</span>
|
||||
</div>
|
||||
<ms-api-auth-config :is-read-only="isReadOnly" :request="authConfig" :encryptShow="false"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item class="expression-link">
|
||||
|
@ -100,11 +123,17 @@ import {cronValidate} from "@/common/js/cron";
|
|||
import {getCurrentProjectID, getCurrentUser, getCurrentWorkspaceId} from "@/common/js/utils";
|
||||
import SelectTree from "@/business/components/common/select-tree/SelectTree";
|
||||
import SwaggerTaskNotification from "@/business/components/api/definition/components/import/SwaggerTaskNotification";
|
||||
import MsApiKeyValue from "../ApiKeyValue";
|
||||
import MsApiVariable from "../ApiVariable";
|
||||
import MsApiAuthConfig from "../auth/ApiAuthConfig";
|
||||
import {REQUEST_HEADERS} from "@/common/js/constants";
|
||||
import {KeyValue} from "../../model/ApiTestModel";
|
||||
import {ELEMENT_TYPE, TYPE_TO_C} from "@/business/components/api/automation/scenario/Setting";
|
||||
|
||||
export default {
|
||||
name: "ApiSchedule",
|
||||
components: {
|
||||
SwaggerTaskNotification, SelectTree, MsFormDivider, SwaggerTaskList, CrontabResult, Crontab
|
||||
SwaggerTaskNotification, SelectTree, MsFormDivider, SwaggerTaskList, CrontabResult, Crontab, MsApiKeyValue, MsApiVariable, MsApiAuthConfig
|
||||
},
|
||||
props: {
|
||||
customValidate: {
|
||||
|
@ -118,7 +147,7 @@ export default {
|
|||
default: false
|
||||
},
|
||||
moduleOptions: Array,
|
||||
param: Object,
|
||||
param: Object
|
||||
},
|
||||
|
||||
watch: {
|
||||
|
@ -180,6 +209,14 @@ export default {
|
|||
id: 'id',
|
||||
label: 'name',
|
||||
},
|
||||
isShowEnable: true,
|
||||
authEnable: false,
|
||||
headerSuggestions: REQUEST_HEADERS,
|
||||
headers: [],
|
||||
queryArguments: [],
|
||||
authConfig: {
|
||||
hashTree: []
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -209,6 +246,7 @@ export default {
|
|||
if (!this.formData.rule) {
|
||||
this.$refs.crontabResult.resultList = [];
|
||||
}
|
||||
this.clearAuthInfo();
|
||||
this.$nextTick(() => {
|
||||
this.$refs.selectTree.init();
|
||||
});
|
||||
|
@ -241,6 +279,12 @@ export default {
|
|||
this.formData.projectId = getCurrentProjectID();
|
||||
this.formData.workspaceId = getCurrentWorkspaceId();
|
||||
this.formData.value = this.formData.rule;
|
||||
// 设置请求头或 query 参数
|
||||
this.formData.headers = this.headers;
|
||||
this.formData.arguments = this.queryArguments;
|
||||
// 设置 BaseAuth 参数
|
||||
this.authConfig.authManager.clazzName = TYPE_TO_C.get("AuthManager");
|
||||
this.formData.authManager = this.authConfig.authManager;
|
||||
let url = '';
|
||||
if (this.formData.id) {
|
||||
url = '/api/definition/schedule/update';
|
||||
|
@ -275,10 +319,32 @@ export default {
|
|||
this.formData.modulePath = data.path;
|
||||
},
|
||||
handleRowClick(row) {
|
||||
// 如果认证信息不为空,进行转化
|
||||
if(row.config != null || row.config != undefined){
|
||||
this.authEnable = true;
|
||||
let config = JSON.parse(row.config);
|
||||
this.headers = config.headers;
|
||||
this.queryArguments = config.arguments;
|
||||
if(config.authManager != null || config.authManager != undefined){
|
||||
this.authConfig = config;
|
||||
}else {
|
||||
this.authConfig = {hashTree: [], authManager: {}};
|
||||
}
|
||||
}else {
|
||||
this.clearAuthInfo();
|
||||
}
|
||||
Object.assign(this.formData, row);
|
||||
this.$nextTick(() => {
|
||||
this.$refs.selectTree.init();
|
||||
});
|
||||
},
|
||||
clearAuthInfo(){
|
||||
this.headers = [];
|
||||
this.queryArguments = [];
|
||||
this.headers.push(new KeyValue({enable: true}));
|
||||
this.queryArguments.push(new KeyValue({enable: true}));
|
||||
this.authConfig = {hashTree: [], authManager: {}};
|
||||
this.authEnable = false;
|
||||
}
|
||||
|
||||
},
|
||||
|
|
|
@ -1464,6 +1464,8 @@ export default {
|
|||
jmeter_tip: "JMX files supporting JMeter 5.2-5.4",
|
||||
suffixFormatErr: "The file format does not meet the requirements",
|
||||
swagger_url_import: "Import using URL",
|
||||
add_request_params: "Add request Parameters",
|
||||
optional: "(optional)",
|
||||
timing_synchronization: "Timing synchronization",
|
||||
next_synchronization_time: "Next synchronization time",
|
||||
ms_env_import_file_limit: "It only supports JSON format files exported through metersphere",
|
||||
|
|
|
@ -1475,6 +1475,8 @@ export default {
|
|||
jmeter_tip: "支持 JMeter5.2-5.4版本的JMX文件",
|
||||
suffixFormatErr: "文件格式不符合要求",
|
||||
swagger_url_import: "使用URL导入",
|
||||
add_request_params: "添加请求参数",
|
||||
optional: "(可选)",
|
||||
timing_synchronization: "定时同步",
|
||||
next_synchronization_time: "下次同步时间",
|
||||
ms_env_import_file_limit: "仅支持通过MeterSphere导出的json格式文件",
|
||||
|
|
|
@ -1473,6 +1473,8 @@ export default {
|
|||
jmeter_tip: "支持 JMeter5.2-5.4版本的JMX文件",
|
||||
suffixFormatErr: "文件格式不符合要求",
|
||||
swagger_url_import: "使用URL導入",
|
||||
add_request_params: "添加請求參數",
|
||||
optional: "(可選)",
|
||||
timing_synchronization: "定時同步",
|
||||
next_synchronization_time: "下次同步時間",
|
||||
ms_env_import_file_limit: "僅支持通過MeterSphere導出的json格式文件",
|
||||
|
|
Loading…
Reference in New Issue