Merge branch 'main' of https://github.com/metersphere/metersphere
This commit is contained in:
commit
87425e8f84
|
@ -1,7 +1,6 @@
|
|||
package io.metersphere.utils;
|
||||
package io.metersphere.commons.utils;
|
||||
|
||||
import io.metersphere.api.dto.definition.BatchDataCopyRequest;
|
||||
import io.metersphere.commons.utils.BeanUtils;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
|
@ -243,7 +243,11 @@ public class JSONUtil {
|
|||
|
||||
public static String parser(String content) {
|
||||
try {
|
||||
Gson gson = new GsonBuilder().setPrettyPrinting().serializeNulls().create();
|
||||
Gson gson = new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.disableHtmlEscaping()
|
||||
.serializeNulls()
|
||||
.create();
|
||||
return gson.toJson(JsonParser.parseString(content).getAsJsonObject());
|
||||
} catch (Exception e) {
|
||||
return content;
|
||||
|
|
|
@ -7,10 +7,14 @@ import io.metersphere.base.domain.PluginExample;
|
|||
import io.metersphere.base.domain.PluginWithBLOBs;
|
||||
import io.metersphere.base.mapper.PluginMapper;
|
||||
import io.metersphere.commons.constants.PluginScenario;
|
||||
import io.metersphere.commons.constants.StorageConstants;
|
||||
import io.metersphere.commons.utils.BeanUtils;
|
||||
import io.metersphere.commons.utils.FileUtils;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.metadata.service.FileManagerService;
|
||||
import io.metersphere.metadata.vo.FileRequest;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
@ -27,6 +31,8 @@ import java.util.stream.Collectors;
|
|||
public class PluginService {
|
||||
@Resource
|
||||
private PluginMapper pluginMapper;
|
||||
@Resource
|
||||
private FileManagerService fileManagerService;
|
||||
|
||||
private boolean isXpack(Class<?> aClass, Object instance) {
|
||||
try {
|
||||
|
@ -37,11 +43,17 @@ public class PluginService {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean loadJar(String jarPath) {
|
||||
private boolean loadJar(String jarPath, String pluginId) {
|
||||
try {
|
||||
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
|
||||
try {
|
||||
File file = new File(jarPath);
|
||||
if (!file.exists()) {
|
||||
// 从MinIO下载
|
||||
if (!this.downPluginJar(jarPath, pluginId, jarPath)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!file.exists()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -61,6 +73,18 @@ public class PluginService {
|
|||
return false;
|
||||
}
|
||||
|
||||
private boolean downPluginJar(String path, String pluginId, String jarPath) {
|
||||
FileRequest request = new FileRequest();
|
||||
request.setProjectId(StringUtils.join(FileUtils.BODY_FILE_DIR, "/plugin", pluginId));
|
||||
request.setFileName(pluginId);
|
||||
request.setStorage(StorageConstants.MINIO.name());
|
||||
byte[] bytes = fileManagerService.downloadFile(request);
|
||||
if (ArrayUtils.isNotEmpty(bytes)) {
|
||||
FileUtils.createFile(path, bytes);
|
||||
}
|
||||
return new File(jarPath).exists();
|
||||
}
|
||||
|
||||
public void loadPlugins() {
|
||||
try {
|
||||
PluginExample example = new PluginExample();
|
||||
|
@ -71,7 +95,7 @@ public class PluginService {
|
|||
-> new TreeSet<>(Comparator.comparing(Plugin::getPluginId))), ArrayList::new));
|
||||
if (CollectionUtils.isNotEmpty(plugins)) {
|
||||
plugins.forEach(item -> {
|
||||
boolean isLoad = this.loadJar(item.getSourcePath());
|
||||
boolean isLoad = this.loadJar(item.getSourcePath(), item.getPluginId());
|
||||
if (!isLoad) {
|
||||
PluginExample pluginExample = new PluginExample();
|
||||
pluginExample.createCriteria().andPluginIdEqualTo(item.getPluginId());
|
||||
|
|
|
@ -50,7 +50,6 @@ import io.metersphere.service.ext.ExtApiScheduleService;
|
|||
import io.metersphere.service.ext.ExtFileAssociationService;
|
||||
import io.metersphere.service.plan.TestPlanApiCaseService;
|
||||
import io.metersphere.service.scenario.ApiScenarioService;
|
||||
import io.metersphere.utils.BatchProcessingUtil;
|
||||
import io.metersphere.xpack.api.service.ApiCaseBatchSyncService;
|
||||
import io.metersphere.xpack.api.service.ApiDefinitionSyncService;
|
||||
import io.metersphere.xpack.quota.service.QuotaService;
|
||||
|
@ -2287,7 +2286,7 @@ public class ApiDefinitionService {
|
|||
saveMockExpectList.forEach(mockExpectConfigBatchMapper::insert);
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(updateMockExpectList)) {
|
||||
updateMockExpectList.forEach(mockExpectConfigBatchMapper::updateByPrimaryKey);
|
||||
updateMockExpectList.forEach(mockExpectConfigBatchMapper::updateByPrimaryKeyWithBLOBs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2312,9 +2311,11 @@ public class ApiDefinitionService {
|
|||
List<ApiTestCaseWithBLOBs> saveCaseList = new ArrayList<>();
|
||||
List<ApiTestCaseWithBLOBs> updateCaseList = new ArrayList<>();
|
||||
Map<String, Integer> lastCaseNumMap = new LinkedHashMap<>();
|
||||
//用例文件关联关系数据 <要复制的用例ID, 生成的用例ID>
|
||||
Map<String, String> forceOverrideFileMap = new HashMap<>();
|
||||
sourceApiCaseList.forEach(item -> {
|
||||
String oldApiId = item.getApiDefinitionId();
|
||||
String refId = sourceApiIdRefIdMap.get(oldApiId);
|
||||
String sourceApiId = item.getApiDefinitionId();
|
||||
String refId = sourceApiIdRefIdMap.get(sourceApiId);
|
||||
if (StringUtils.isNotBlank(refId)) {
|
||||
ApiDefinition api = refIdMap.get(refId);
|
||||
if (api != null) {
|
||||
|
@ -2349,15 +2350,22 @@ public class ApiDefinitionService {
|
|||
newCase.setUpdateTime(timeStamp);
|
||||
updateCaseList.add(newCase);
|
||||
}
|
||||
|
||||
forceOverrideFileMap.put(item.getId(), newCase.getId());
|
||||
//本地文件覆盖
|
||||
FileUtils.forceOverrideBodyFiles(item.getId(), newCase.getId());
|
||||
}
|
||||
}
|
||||
});
|
||||
FileAssociationMapper batchFileAssociationMapper = batchSqlSession.getMapper(FileAssociationMapper.class);
|
||||
extFileAssociationService.forceOverrideFileAssociation(forceOverrideFileMap, batchFileAssociationMapper);
|
||||
|
||||
ApiTestCaseMapper apiTestCaseBatchMapper = batchSqlSession.getMapper(ApiTestCaseMapper.class);
|
||||
if (CollectionUtils.isNotEmpty(saveCaseList)) {
|
||||
saveCaseList.forEach(apiTestCaseBatchMapper::insert);
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(updateCaseList)) {
|
||||
updateCaseList.forEach(apiTestCaseBatchMapper::updateByPrimaryKey);
|
||||
updateCaseList.forEach(apiTestCaseBatchMapper::updateByPrimaryKeyWithBLOBs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,10 +5,15 @@ import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
|
|||
import io.metersphere.api.dto.definition.request.variable.ScenarioVariable;
|
||||
import io.metersphere.api.dto.scenario.Body;
|
||||
import io.metersphere.api.dto.scenario.KeyValue;
|
||||
import io.metersphere.base.domain.FileAssociation;
|
||||
import io.metersphere.base.domain.FileAssociationExample;
|
||||
import io.metersphere.base.mapper.FileAssociationMapper;
|
||||
import io.metersphere.commons.enums.FileAssociationTypeEnums;
|
||||
import io.metersphere.commons.utils.BeanUtils;
|
||||
import io.metersphere.metadata.service.FileAssociationService;
|
||||
import io.metersphere.plugin.core.MsTestElement;
|
||||
import io.metersphere.request.BodyFile;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -16,6 +21,8 @@ import org.springframework.util.CollectionUtils;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
|
@ -76,4 +83,38 @@ public class ExtFileAssociationService extends FileAssociationService {
|
|||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制覆盖文件关系
|
||||
*
|
||||
* @param overrideIdMap <来源ID - 强制覆盖的ID>
|
||||
* @param batchProcessingMapper 批量处理Mapper, 在该方法调用地方进行事务提交
|
||||
*/
|
||||
public void forceOverrideFileAssociation(Map<String, String> overrideIdMap, FileAssociationMapper batchProcessingMapper) {
|
||||
if (MapUtils.isEmpty(overrideIdMap) || batchProcessingMapper == null) {
|
||||
return;
|
||||
}
|
||||
//删除原来的数据
|
||||
FileAssociationExample example = new FileAssociationExample();
|
||||
example.createCriteria().andSourceIdIn(new ArrayList<>(overrideIdMap.values()));
|
||||
batchProcessingMapper.deleteByExample(example);
|
||||
|
||||
example.clear();
|
||||
example.createCriteria().andSourceIdIn(new ArrayList<>(overrideIdMap.keySet()));
|
||||
List<FileAssociation> fileAssociationList = batchProcessingMapper.selectByExample(example);
|
||||
List<FileAssociation> saveList = new ArrayList<>();
|
||||
fileAssociationList.forEach(item -> {
|
||||
String overrideId = overrideIdMap.get(item.getSourceId());
|
||||
if (StringUtils.isNotBlank(overrideId)) {
|
||||
FileAssociation overrideFileAssociation = new FileAssociation();
|
||||
BeanUtils.copyBean(overrideFileAssociation, item);
|
||||
overrideFileAssociation.setId(UUID.randomUUID().toString());
|
||||
overrideFileAssociation.setSourceId(overrideId);
|
||||
saveList.add(overrideFileAssociation);
|
||||
}
|
||||
});
|
||||
|
||||
saveList.forEach(batchProcessingMapper::insert);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -275,7 +275,7 @@
|
|||
</div>
|
||||
</el-form>
|
||||
<p class="tip">{{ $t('schema.preview') }}</p>
|
||||
<pre style="width: 100%">{{ completeNodeValue }}</pre>
|
||||
<pre style="width: 100%; white-space: pre-wrap;">{{ completeNodeValue }}</pre>
|
||||
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<ms-dialog-footer @cancel="modalVisible = false" @confirm="handleOk" />
|
||||
|
|
|
@ -12,17 +12,26 @@
|
|||
<el-select v-model="versionId" size="small" :placeholder="$t('project.version.please_input_version')">
|
||||
<el-option v-for="v in versionData" :key="v.id" :label="v.name" :value="v.id" />
|
||||
</el-select>
|
||||
<el-popover
|
||||
placement="top-start"
|
||||
width="200"
|
||||
trigger="hover"
|
||||
:content="$t('api_definition.copy_data_from_other_version_tips')">
|
||||
<i class="el-icon-warning" slot="reference" style="color: #f56c6c; margin: 0 0 0 5px" />
|
||||
</el-popover>
|
||||
</el-row>
|
||||
<el-row style="margin-top: 10px">
|
||||
<el-checkbox v-model="selectCase" v-permission="['PROJECT_API_DEFINITION:READ+CREATE_CASE']">{{ $t('commons.api_case') }}</el-checkbox>
|
||||
<el-checkbox v-model="selectCase" v-permission="['PROJECT_API_DEFINITION:READ+CREATE_CASE']">{{
|
||||
$t('commons.api_case')
|
||||
}}</el-checkbox>
|
||||
<el-checkbox v-model="selectMock">{{ $t('commons.mock') }}</el-checkbox>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-row>
|
||||
<template v-slot:footer>
|
||||
<el-button type="primary" :loading="saving" size="small" @click="save" @keydown.enter.native.prevent>{{
|
||||
$t('commons.save')
|
||||
}}</el-button>
|
||||
<el-button type="primary" :loading="saving" size="small" @click="save" @keydown.enter.native.prevent>{{
|
||||
$t('commons.save')
|
||||
}}</el-button>
|
||||
</template>
|
||||
</ms-edit-dialog>
|
||||
</template>
|
||||
|
@ -37,7 +46,7 @@ export default {
|
|||
return {
|
||||
loading: false,
|
||||
visible: false,
|
||||
saving:false,
|
||||
saving: false,
|
||||
versionId: '',
|
||||
versionData: [],
|
||||
selectCase: true,
|
||||
|
@ -82,7 +91,7 @@ export default {
|
|||
this.$error(this.$t('project.version.please_input_version'));
|
||||
} else {
|
||||
this.saving = true;
|
||||
this.$emit('handleSave', this.versionId,this.selectCase,this.selectMock);
|
||||
this.$emit('handleSave', this.versionId, this.selectCase, this.selectMock);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
@ -19,6 +19,8 @@ const message = {
|
|||
default_value: 'Default value',
|
||||
},
|
||||
copy_data_from_other_version: 'Copy data from other version',
|
||||
copy_data_from_other_version_tips:
|
||||
'Use cases with the same name and mock expectations will be forcibly overwritten!',
|
||||
body: {
|
||||
json_format_error: 'JSON format error',
|
||||
},
|
||||
|
|
|
@ -18,6 +18,7 @@ const message = {
|
|||
default_value: '默认值',
|
||||
},
|
||||
copy_data_from_other_version: '复制版本数据',
|
||||
copy_data_from_other_version_tips: '名称相同的用例和Mock期望会进行强制覆盖!',
|
||||
body: {
|
||||
json_format_error: 'JSON格式错误',
|
||||
},
|
||||
|
|
|
@ -18,6 +18,7 @@ const message = {
|
|||
default_value: '默認值',
|
||||
},
|
||||
copy_data_from_other_version: '複製版本數據',
|
||||
copy_data_from_other_version_tips: '名稱相同的用例和Mock期望會進行強制覆蓋!',
|
||||
body: {
|
||||
json_format_error: 'JSON格式錯誤',
|
||||
},
|
||||
|
|
|
@ -171,6 +171,18 @@ public class FileUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制覆盖文件
|
||||
*
|
||||
* @param sourceId 源ID
|
||||
* @param targetId 目标ID
|
||||
*/
|
||||
public static void forceOverrideBodyFiles(String sourceId, String targetId) {
|
||||
//删除源文件
|
||||
deleteBodyFiles(targetId);
|
||||
copyBodyFiles(sourceId, targetId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制文件夹(使用缓冲字节流)
|
||||
*
|
||||
|
|
|
@ -5,14 +5,18 @@ import io.metersphere.base.domain.PluginExample;
|
|||
import io.metersphere.base.domain.PluginWithBLOBs;
|
||||
import io.metersphere.base.mapper.PluginMapper;
|
||||
import io.metersphere.commons.constants.PluginScenario;
|
||||
import io.metersphere.commons.constants.StorageConstants;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.BeanUtils;
|
||||
import io.metersphere.commons.utils.FileUtils;
|
||||
import io.metersphere.commons.utils.JSON;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.log.utils.ReflexObjectUtil;
|
||||
import io.metersphere.log.vo.DetailColumn;
|
||||
import io.metersphere.log.vo.OperatingLogDetails;
|
||||
import io.metersphere.log.vo.system.SystemReference;
|
||||
import io.metersphere.metadata.service.FileManagerService;
|
||||
import io.metersphere.metadata.vo.FileRequest;
|
||||
import io.metersphere.request.PluginDTO;
|
||||
import io.metersphere.request.PluginRequest;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
|
@ -33,6 +37,8 @@ public class PluginService {
|
|||
private PlatformPluginService platformPluginService;
|
||||
@Resource
|
||||
private ApiPluginService apiPluginService;
|
||||
@Resource
|
||||
private FileManagerService fileManagerService;
|
||||
|
||||
public void addPlugin(PluginWithBLOBs plugin) {
|
||||
if (StringUtils.isBlank(plugin.getId())) {
|
||||
|
@ -91,6 +97,8 @@ public class PluginService {
|
|||
platformPluginService.delete(id);
|
||||
} else {
|
||||
// 接口传的是 pluginId
|
||||
FileRequest request = getRequest(id);
|
||||
fileManagerService.delete(request);
|
||||
apiPluginService.delete(id);
|
||||
}
|
||||
}
|
||||
|
@ -119,9 +127,23 @@ public class PluginService {
|
|||
} else {
|
||||
List<PluginWithBLOBs> plugins = apiPluginService.addApiPlugin(file);
|
||||
plugins.forEach(this::addPlugin);
|
||||
// 存入MinIO
|
||||
if (CollectionUtils.isNotEmpty(plugins)) {
|
||||
String pluginId = plugins.get(0).getPluginId();
|
||||
FileRequest request = getRequest(pluginId);
|
||||
fileManagerService.upload(file, request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private FileRequest getRequest(String pluginId) {
|
||||
FileRequest request = new FileRequest();
|
||||
request.setProjectId(StringUtils.join(FileUtils.BODY_FILE_DIR, "/plugin", pluginId));
|
||||
request.setFileName(pluginId);
|
||||
request.setStorage(StorageConstants.MINIO.name());
|
||||
return request;
|
||||
}
|
||||
|
||||
public void checkPluginExist(MultipartFile file) {
|
||||
String filename = file.getOriginalFilename();
|
||||
PluginExample example = new PluginExample();
|
||||
|
@ -137,7 +159,7 @@ public class PluginService {
|
|||
example.createCriteria().andPluginIdEqualTo(id);
|
||||
List<PluginWithBLOBs> plugins = pluginMapper.selectByExampleWithBLOBs(example);
|
||||
if (CollectionUtils.isNotEmpty(plugins)) {
|
||||
Plugin plugin = plugins.get(0);
|
||||
Plugin plugin = plugins.get(0);
|
||||
List<DetailColumn> columns = ReflexObjectUtil.getColumns(plugin, SystemReference.pluginColumns);
|
||||
OperatingLogDetails details = new OperatingLogDetails(JSON.toJSONString(plugin.getId()), null, plugin.getSourceName(), plugin.getCreateUserId(), columns);
|
||||
return JSON.toJSONString(details);
|
||||
|
|
Loading…
Reference in New Issue