refactor(接口测试): 优化导入逻辑 (#15290)

--task=1008278 --user=王孝刚 接口测试导入逻辑优化(1.20同步上)
https://www.tapd.cn/55049933/s/1187315

Co-authored-by: wxg0103 <727495428@qq.com>
This commit is contained in:
MeterSphere Bot 2022-06-27 16:17:27 +08:00 committed by GitHub
parent 35a8ef33bb
commit 1b45326a54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 735 additions and 654 deletions

View File

@ -35,5 +35,9 @@ public class ApiTestImportRequest {
private List<KeyValue> headers;
private List<KeyValue> arguments;
private MsAuthManager authManager;
// 是否覆盖模块
private Boolean coverModule;
// 当前协议
private String protocol;
}

View File

@ -76,6 +76,8 @@ public class MsDefinitionParser extends MsAbstractParser<ApiDefinitionImport> {
private ApiDefinitionImport parseMsFormat(String testStr, ApiTestImportRequest importRequest) {
ApiDefinitionImport apiDefinitionImport = JSON.parseObject(testStr, ApiDefinitionImport.class, Feature.DisableSpecialKeyDetect);
List<ApiDefinitionWithBLOBs> protocol = apiDefinitionImport.getData().stream().filter(item -> StringUtils.equals(importRequest.getProtocol(), item.getProtocol())).collect(Collectors.toList());
apiDefinitionImport.setData(protocol);
Map<String, List<ApiTestCaseWithBLOBs>> caseMap = new HashMap<>();
if (apiDefinitionImport.getCases() != null) {
apiDefinitionImport.getCases().forEach(item -> {

View File

@ -675,7 +675,8 @@ public class ApiAutomationService {
.andProjectIdEqualTo(request.getProjectId())
.andStatusNotEqualTo("Trash")
.andIdNotEqualTo(request.getId())
.andVersionIdEqualTo(request.getVersionId());
.andVersionIdEqualTo(request.getVersionId())
.andApiScenarioModuleIdEqualTo(request.getApiScenarioModuleId());
if (apiScenarioMapper.countByExample(example) > 0) {
MSException.throwException(Translator.get("automation_name_already_exists"));
}

View File

@ -524,7 +524,7 @@ public class ApiDefinitionService {
criteria.andMethodEqualTo(request.getMethod()).andStatusNotEqualTo("Trash")
.andProtocolEqualTo(request.getProtocol()).andPathEqualTo(request.getPath())
.andProjectIdEqualTo(request.getProjectId()).andIdNotEqualTo(request.getId())
.andVersionIdEqualTo(request.getVersionId());
.andVersionIdEqualTo(request.getVersionId()).andModuleIdEqualTo(request.getModuleId());
Project project = projectMapper.selectByPrimaryKey(request.getProjectId());
ProjectConfig config = projectApplicationService.getSpecificTypeValue(project.getId(), ProjectApplicationType.URL_REPEATABLE.name());
boolean urlRepeat = config.getUrlRepeatable();
@ -541,7 +541,7 @@ public class ApiDefinitionService {
} else {
example.createCriteria().andProtocolEqualTo(request.getProtocol()).andStatusNotEqualTo("Trash")
.andNameEqualTo(request.getName()).andProjectIdEqualTo(request.getProjectId())
.andIdNotEqualTo(request.getId()).andVersionIdEqualTo(request.getVersionId());
.andIdNotEqualTo(request.getId()).andVersionIdEqualTo(request.getVersionId()).andModuleIdEqualTo(request.getModuleId());
if (apiDefinitionMapper.countByExample(example) > 0) {
MSException.throwException(Translator.get("load_test_already_exists"));
}

View File

@ -22,12 +22,17 @@
<el-row>
<el-col :span="11">
<el-form-item :label="$t('commons.import_module')">
<ms-select-tree size="small" :data="moduleOptions" :defaultKey="formData.moduleId" @getValue="setModule" :obj="moduleObj" clearable checkStrictly/>
<ms-select-tree size="small" :data="moduleOptions" :defaultKey="formData.moduleId" @getValue="setModule"
:obj="moduleObj" clearable checkStrictly/>
</el-form-item>
<el-form-item v-if="!isHar" :label="$t('commons.import_mode')">
<el-select size="small" v-model="formData.modeId" class="project-select" clearable>
<el-option v-for="item in modeOptions" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
<el-checkbox size="mini" v-if="formData.modeId === 'fullCoverage'"
v-model="formData.coverModule">
{{ this.$t('commons.cover_scenario') }}
</el-checkbox>
</el-form-item>
<el-form-item v-xpack v-if="projectVersionEnable && formData.modeId === 'incrementalMerge'"
:label="$t('api_test.api_import.import_version')" prop="versionId">
@ -81,10 +86,14 @@
</div>
<div>
<span>
{{ $t('api_test.api_import.import_cover_tip') }}<br/>
{{ $t('api_test.api_import.cover_tip_1') }}<br/>
{{ $t('api_test.api_import.cover_tip_2') }}<br/>
{{ $t('api_test.api_import.cover_tip_3') }}
{{ $t('api_test.api_import.cover_tip') }} :<br/>
{{ $t('api_test.api_import.cover_tip_scenario_1') }}<br/>
{{ $t('api_test.api_import.cover_tip_scenario_2') }}<br/>
{{ $t('api_test.api_import.cover_tip_scenario_3') }}<br/>
{{ $t('api_test.api_import.cover_tip_scenario_4') }}<br/>
{{ $t('api_test.api_import.no_cover_tip') }} :<br/>
{{ $t('api_test.api_import.no_cover_tip_scenario_1') }}<br/>
{{ $t('api_test.api_import.no_cover_tip_scenario_2') }}
</span>
</div>
</div>
@ -162,6 +171,7 @@ export default {
swaggerUrl: '',
modeId: 'incrementalMerge',
moduleId: '',
coverModule: false
},
rules: {},
currentModule: {},
@ -174,12 +184,10 @@ export default {
projectVersionEnable: false,
}
},
activated() {
this.selectedPlatform = this.platforms[0];
},
created() {
this.getVersionOptions();
this.checkVersionEnable();
this.selectedPlatform = this.platforms[0];
},
watch: {
selectedPlatformValue() {
@ -396,4 +404,5 @@ export default {
height: 200px;
}
</style>

View File

@ -32,6 +32,10 @@
<el-select size="small" v-model="formData.modeId" clearable style="width: 100%">
<el-option v-for="item in modeOptions" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
<el-checkbox size="mini" v-if="formData.modeId === 'fullCoverage'" v-model="formData.coverModule"
style="display:block;">
{{ this.$t('commons.cover_api') }}
</el-checkbox>
</el-form-item>
<el-form-item v-xpack v-if="projectVersionEnable && formData.modeId === 'incrementalMerge'"
:label="$t('api_test.api_import.import_version')" prop="versionId">
@ -82,12 +86,14 @@
<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"/>
<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"/>
<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>
@ -125,10 +131,18 @@
</div>
<div>
<span>
{{ $t('api_test.api_import.import_cover_tip') }}<br/>
{{ $t('api_test.api_import.import_tip') }} : <br/>
{{ $t('api_test.api_import.import_tip1') }} <br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ $t('api_test.api_import.import_tip2') }} <br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ $t('api_test.api_import.import_tip3') }} <br/>
{{ $t('api_test.api_import.cover_tip') }} :<br/>
{{ $t('api_test.api_import.cover_tip_1') }}<br/>
{{ $t('api_test.api_import.cover_tip_2') }}<br/>
{{ $t('api_test.api_import.cover_tip_3') }}
{{ $t('api_test.api_import.cover_tip_3') }}<br/>
{{ $t('api_test.api_import.cover_tip_4') }}<br/>
{{ $t('api_test.api_import.no_cover_tip') }} :<br/>
{{ $t('api_test.api_import.no_cover_tip_1') }}<br/>
{{ $t('api_test.api_import.no_cover_tip_2') }}
</span>
</div>
</div>
@ -161,7 +175,7 @@ export default {
default: true,
},
moduleOptions: Array,
propotal: String,
protocol: String,
model: {
type: String,
default: 'definition'
@ -189,7 +203,6 @@ export default {
name: this.$t('commons.not_cover')
}
],
protocol: "",
platforms: [
{
name: 'MeterSphere',
@ -244,7 +257,8 @@ export default {
file: undefined,
swaggerUrl: '',
modeId: 'incrementalMerge',
moduleId: ''
moduleId: '',
coverModule: false
},
rules: {
modeId: [
@ -291,7 +305,7 @@ export default {
this.formData.modeId = 'fullCoverage';
}
},
propotal() {
protocol() {
let postmanIndex = this.platforms.indexOf(this.postmanPlanform);
let swaggerPlanformIndex = this.platforms.indexOf(this.swaggerPlanform);
let harPlanformIndex = this.platforms.indexOf(this.harPlanform);
@ -308,12 +322,12 @@ export default {
if (esbPlanformIndex >= 0) {
this.platforms.splice(this.platforms.indexOf(this.esbPlanform), 1);
}
if (this.propotal === 'TCP') {
if (this.protocol === 'TCP') {
if (hasLicense()) {
this.platforms.push(this.esbPlanform);
}
return true;
} else if (this.propotal === 'HTTP') {
} else if (this.protocol === 'HTTP') {
this.platforms.push(this.postmanPlanform);
this.platforms.push(this.swaggerPlanform);
this.platforms.push(this.harPlanform);
@ -432,6 +446,7 @@ export default {
param.modeId = this.formData.modeId
}
param.projectId = this.projectId;
param.protocol = this.protocol;
if (!this.swaggerUrlEnable) {
param.swaggerUrl = undefined;
}
@ -557,4 +572,5 @@ export default {
height: 200px;
}
</style>

View File

@ -28,7 +28,7 @@
@saveAsEdit="saveAsEdit"
@refresh="refresh"
ref="basisApi"/>
<api-import :propotal="condition.protocol" ref="apiImport" :moduleOptions="moduleOptions"
<api-import :protocol="condition.protocol" ref="apiImport" :moduleOptions="moduleOptions"
@refresh="$emit('refresh')"/>
</div>
</template>

View File

@ -18,6 +18,8 @@ export default {
pass_rate: 'Pass Rate',
execution_times: 'Execution Times',
cover: 'Cover',
cover_api: 'Synchronously override API modules',
cover_scenario: 'Synchronous overlay scene module',
module_title: 'Default module',
save_data_when_page_change: 'Save when page change',
not_cover: 'Not Cover',
@ -1773,9 +1775,24 @@ export default {
file_exceed_limit: "The number of files exceeds the limit",
import_cover_tip: "Import mode: Overwrite mode description",
file_name_to_long: "File name is too long",
cover_tip_1: "1. Add if the interface path does not exist",
cover_tip_2: "2. The interface path is consistent with the original interface, if the content is inconsistent, the original interface will be overwritten",
cover_tip_3: "3. If the interface path and content are consistent with the original interface, no change will be made",
import_tip: "Import Instructions",
import_tip1: "Note: The import file contains a variety of protocols, you need to switch the protocol to import multiple times",
import_tip2: "If the URL can be repeated, if the interface name + request type + request path are consistent, it is judged to be the same interface",
import_tip3: "If the URL is not enabled, it can be repeated. If the request type + request path are consistent, it is judged to be the same interface",
cover_tip: "Override mode",
cover_tip_1: "1. If the \"Synchronously overwrite API module\" option is checked, the API module is the module specified in the import file",
cover_tip_2: "2. The same interface already exists in the system, if the content is inconsistent, the original interface of the system will be overwritten",
cover_tip_3: "3. The same interface that already exists in the system will not be changed if the content is the same",
cover_tip_4: "4. Add interfaces that do not exist in the system",
cover_tip_scenario_1: "1. If the \"Synchronously overwrite Scenario module\" option is checked, the Scenario module is the module specified in the import file",
cover_tip_scenario_2: "2. The same Scenario already exists in the system, if the content is inconsistent, the original Scenario of the system will be overwritten",
cover_tip_scenario_3: "3. The same Scenario that already exists in the system will not be changed if the content is the same",
cover_tip_scenario_4: "4. Add Scenario that do not exist in the system",
no_cover_tip: "No overlay mode",
no_cover_tip_1: "1. The same interface that already exists in the system will not be changed",
no_cover_tip_2: "2. Add interfaces that do not exist in the system",
no_cover_tip_scenario_1: "1. The same Scenario that already exists in the system will not be changed",
no_cover_tip_scenario_2: "2. Add Scenario that do not exist in the system",
import_version: 'Import version',
data_update_version: 'Api update version',
data_new_version: 'Api creation version',

View File

@ -18,6 +18,8 @@ export default {
pass_rate: '通过率',
execution_times: '执行次数',
cover: '覆盖',
cover_api: '同步覆盖API模块',
cover_scenario: '同步覆盖场景模块',
module_title: '默认模块',
save_data_when_page_change: '翻页保存勾选项',
not_cover: '不覆盖',
@ -1780,10 +1782,24 @@ export default {
next_synchronization_time: "下次同步时间",
ms_env_import_file_limit: "仅支持通过MeterSphere导出的json格式文件",
file_exceed_limit: "文件数量超出限制",
import_cover_tip: "导入模式: 覆盖模式说明",
cover_tip_1: "1. 接口路径不存在则新增",
cover_tip_2: "2. 接口路径与原接口一致,内容不一致则覆盖原接口",
cover_tip_3: "3. 接口路径、内容与原接口一致则不做变更",
import_tip: "导入说明",
import_tip1: "注:导入文件包含多种协议,需切换协议多次导入",
import_tip2: "开启URL可重复接口名称+请求类型+请求路径一致则判断为同一接口",
import_tip3: "未开启URL可重复请求类型+请求路径一致则判断为同一接口",
cover_tip: "覆盖模式",
cover_tip_1: "1. 如勾选“同步覆盖API模块”选项则API模块为导入文件中指定的模块",
cover_tip_2: "2. 系统已存在的同一接口,内容不一致则覆盖系统原接口",
cover_tip_3: "3. 系统已存在的同一接口,内容一致则不做变更",
cover_tip_4: "4. 系统不存在的接口则新增",
cover_tip_scenario_1: "1. 如勾选“同步覆盖场景模块”选项,则场景模块为导入文件中指定的模块",
cover_tip_scenario_2: "2. 系统已存在的同一场景,内容不一致则覆盖系统原场景",
cover_tip_scenario_3: "3. 系统已存在的同一场景,内容一致则不做变更",
cover_tip_scenario_4: "4. 系统不存在的场景则新增",
no_cover_tip: "不覆盖模式",
no_cover_tip_1: "1. 系统已存在的同一接口,则不做变更",
no_cover_tip_2: "2. 系统不存在的接口则新增",
no_cover_tip_scenario_1: "1. 系统已存在的同一场景,则不做变更",
no_cover_tip_scenario_2: "2. 系统不存在的场景则新增",
import_version: '导入版本',
data_update_version: '数据更新版本',
data_new_version: '数据创建版本',

View File

@ -18,6 +18,8 @@ export default {
pass_rate: '通過率',
execution_times: '執行次數',
cover: '覆蓋',
cover_api: '同步覆蓋API模塊',
cover_scenario: '同步覆蓋場景模塊',
module_title: '默認模塊',
save_data_when_page_change: '翻頁保存勾選項',
not_cover: '不覆蓋',
@ -1777,10 +1779,24 @@ export default {
next_synchronization_time: "下次同步時間",
ms_env_import_file_limit: "僅支持通過MeterSphere導出的json格式文件",
file_exceed_limit: "文件數量超出限製",
import_cover_tip: "導入模式: 覆蓋模式說明",
cover_tip_1: "1. 接口路徑不存在則新增",
cover_tip_2: "2. 接口路徑與原接口一致,內容不一致則覆蓋原接口",
cover_tip_3: "3. 接口路徑、內容與原接口一致則不做變更",
import_tip: "導入說明",
import_tip1: "注:導入檔案包含多種協定,需切換協定多次導入",
import_tip2: "開啟URL可重複介面名稱+請求類型+請求路徑一致則判斷為同一介面",
import_tip3: "未開啟URL可重複請求類型+請求路徑一致則判斷為同一介面",
cover_tip: "覆蓋模式",
cover_tip_1: "1. 如勾選“同步覆蓋API模塊”選項則API模塊為導入檔案中指定的模塊",
cover_tip_2: "2. 系統已存在的同一介面,內容不一致則覆蓋系統原介面",
cover_tip_3: "3. 系統已存在的同一介面,內容一致則不做變更",
cover_tip_4: "4. 系統不存在的介面則新增",
cover_tip_scenario_1: "1. 如勾選“同步覆蓋場景模塊”選項,則場景模塊為導入檔案中指定的模塊",
cover_tip_scenario_2: "2. 系統已存在的同一場景,內容不一致則覆蓋系統原場景",
cover_tip_scenario_3: "3. 系統已存在的同一場景,內容一致則不做變更",
cover_tip_scenario_4: "4. 系統不存在的場景則新增",
no_cover_tip_scenario_1: "1. 系统已存在的同一場景,则不做變更",
no_cover_tip_scenario_2: "2. 系统不存在的場景则新增",
no_cover_tip: "不覆蓋模式",
no_cover_tip_1: "1. 系統已存在的同一介面,則不做變更",
no_cover_tip_2: "2. 系統不存在的介面則新增",
import_version: '導入版本',
data_update_version: '數據更新版本',
data_new_version: '數據創建版本',