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:
ZJH 2021-12-09 14:31:52 +08:00 committed by jianxing
parent afb0328a8b
commit 43eb75c26f
18 changed files with 438 additions and 74 deletions

View File

@ -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;
}

View File

@ -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();

View File

@ -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();

View File

@ -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);
}
/**
* 列表开关切换
*

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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>

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}
}
}
}

View File

@ -0,0 +1,2 @@
-- 新增字段
ALTER TABLE `swagger_url_project` ADD COLUMN `config` longtext COMMENT '鉴权配置信息' AFTER `mode_id`;

View File

@ -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;
}
}
}
}

View File

@ -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) {

View File

@ -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;
},

View File

@ -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;
}
},

View File

@ -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",

View File

@ -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格式文件",

View File

@ -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格式文件",