feat(接口自动化): 标签定义

This commit is contained in:
fit2-zhao 2020-12-22 16:49:47 +08:00
parent 2cf68db349
commit ef2412a3e2
7 changed files with 174 additions and 245 deletions

View File

@ -73,25 +73,6 @@ public class ApiAutomationService {
List<ApiScenarioDTO> list = extApiScenarioMapper.list(request);
ApiTagExample example = new ApiTagExample();
example.createCriteria().andProjectIdEqualTo(request.getProjectId());
List<ApiTag> tags = apiTagMapper.selectByExample(example);
Map<String, String> tagMap = tags.stream().collect(Collectors.toMap(ApiTag::getId, ApiTag::getName));
Gson gs = new Gson();
list.forEach(item -> {
if (item.getTagId() != null) {
StringBuilder buf = new StringBuilder();
gs.fromJson(item.getTagId(), List.class).forEach(t -> {
buf.append(tagMap.get(t));
buf.append(",");
});
if (buf != null && buf.length() > 0) {
String tagNames = buf.toString().substring(0, buf.toString().length() - 1);
List<String> tagList = Arrays.asList(tagNames.split(","));
item.setTagNames(tagList);
} else {
item.setTagNames(new ArrayList<>());
}
}
});
return list;
}

View File

@ -58,14 +58,4 @@ CREATE TABLE `api_scenario_report_detail` (
`project_id` varchar(64) NOT NULL COMMENT 'scenario ID',
`content` longblob COMMENT 'Report Content',
PRIMARY KEY (`report_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `api_tag` (
`id` varchar(50) NOT NULL COMMENT 'Test ID',
`project_id` varchar(50) NOT NULL COMMENT 'Project ID this test belongs to',
`name` varchar(200) NOT NULL COMMENT 'api tag',
`user_id` varchar(64) DEFAULT NULL COMMENT 'User ID',
`create_time` bigint(13) NOT NULL COMMENT 'Create timestamp',
`update_time` bigint(13) NOT NULL COMMENT 'Update timestamp',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

View File

@ -17,29 +17,30 @@
"@fortawesome/vue-fontawesome": "^0.1.9",
"axios": "^0.19.0",
"core-js": "^3.4.3",
"diffable-html": "^4.0.0",
"echarts": "^4.6.0",
"el-table-infinite-scroll": "^1.0.10",
"element-ui": "^2.13.0",
"html2canvas": "^1.0.0-rc.7",
"js-base64": "^3.4.4",
"json-bigint": "^1.0.0",
"jsoneditor": "^9.1.2",
"jspdf": "^2.1.1",
"md5": "^2.3.0",
"mockjs": "^1.1.0",
"nprogress": "^0.2.0",
"sha.js": "^2.4.11",
"vue": "^2.6.10",
"vue-calendar-heatmap": "^0.8.4",
"vue-echarts": "^4.1.0",
"vue-i18n": "^8.15.3",
"vue-input-tag": "^2.0.7",
"vue-pdf": "^4.2.0",
"vue-router": "^3.1.3",
"vuedraggable": "^2.23.2",
"vuex": "^3.1.2",
"vue-calendar-heatmap": "^0.8.4",
"mockjs": "^1.1.0",
"md5": "^2.3.0",
"sha.js": "^2.4.11",
"js-base64": "^3.4.4",
"json-bigint": "^1.0.0",
"html2canvas": "^1.0.0-rc.7",
"jspdf": "^2.1.1",
"yan-progress": "^1.0.3",
"nprogress": "^0.2.0",
"el-table-infinite-scroll": "^1.0.10",
"vue-pdf": "^4.2.0",
"diffable-html": "^4.0.0",
"xml-js": "^1.6.11",
"jsoneditor": "^9.1.2"
"yan-progress": "^1.0.3"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.1.0",

View File

@ -1,126 +0,0 @@
<template>
<el-dialog :close-on-click-modal="false" :title="$t('api_test.automation.create_tag')" :visible.sync="visible"
width="45%"
:destroy-on-close="true">
<el-form :model="tagForm" label-position="right" label-width="80px" size="small" :rules="rule" ref="tagForm">
<el-form-item :label="$t('commons.name')" prop="name">
<el-row>
<el-col :span="20">
<el-input v-model="tagForm.name" autocomplete="off" :placeholder="$t('commons.name')"/>
</el-col>
<el-col :span="4">
<el-button style="margin-left: 20px" @click="saveTag">{{$t('commons.save')}}</el-button>
</el-col>
</el-row>
</el-form-item>
</el-form>
<el-table :data="tagData" row-key="id">
<el-table-column prop="name" :label="$t('commons.name')" show-overflow-tooltip/>
<el-table-column :label="$t('commons.operating')" min-width="130" align="center">
<template v-slot:default="scope">
<el-button type="text" @click="editApi(scope.row)">{{$t('commons.edit')}}</el-button>
<el-button type="text" @click="handleDelete(scope.row)" style="color: #F56C6C">{{$t('commons.delete')}}</el-button>
</template>
</el-table-column>
</el-table>
<ms-table-pagination :change="initTable" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
<template v-slot:footer>
<ms-dialog-footer
@cancel="confirm"
@confirm="confirm">
</ms-dialog-footer>
</template>
</el-dialog>
</template>
<script>
import {WORKSPACE_ID} from '@/common/js/constants';
import {getCurrentUser, getUUID, getCurrentProjectID} from "@/common/js/utils";
import MsDialogFooter from "@/business/components/common/components/MsDialogFooter";
import MsTablePagination from "../../../common/pagination/TablePagination";
export default {
name: "MsAddTag",
components: {MsDialogFooter, MsTablePagination},
props: {},
data() {
return {
tagForm: {},
visible: false,
currentModule: {},
projectId: "",
userOptions: [],
currentPage: 1,
pageSize: 10,
total: 0,
tagData: [],
path: "/api/tag/create",
rule: {
name: [
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
{max: 50, message: this.$t('test_track.length_less_than') + '50', trigger: 'blur'}
],
principal: [{
required: true,
message: this.$t('api_test.automation.scenario.select_principal'),
trigger: 'change'
}],
},
}
},
methods: {
saveTag() {
if (this.tagForm.id != undefined && this.tagForm.id != null) {
this.path = "/api/tag/update";
} else {
this.path = "/api/tag/create";
this.tagForm.id = getUUID();
}
this.$refs['tagForm'].validate((valid) => {
if (valid) {
this.setParameter();
this.result = this.$post(this.path, this.tagForm, () => {
this.$success(this.$t('commons.save_success'));
this.initTable();
this.tagForm = {};
});
} else {
return false;
}
})
},
setParameter() {
this.tagForm.projectId = this.projectId;
},
open() {
this.projectId = getCurrentProjectID();
this.visible = true;
this.initTable();
},
initTable() {
let condition = {};
condition.projectId = this.projectId;
this.result = this.$post("/api/tag/getTgas/" + this.currentPage + "/" + this.pageSize, condition, response => {
this.total = response.data.itemCount;
this.tagData = response.data.listObject;
});
},
editApi(row) {
this.tagForm = row;
},
handleDelete(row) {
this.result = this.$get("/api/tag/delete/" + row.id, response => {
this.$success(this.$t('commons.delete_success'));
this.initTable();
});
},
confirm() {
this.visible = false
this.$emit('refreshTags');
}
}
}
</script>

View File

@ -25,9 +25,9 @@
</template>
</el-table-column>
<el-table-column prop="tagNames" :label="$t('api_test.automation.tag')" width="200px">
<el-table-column prop="tagId" :label="$t('api_test.automation.tag')" width="200px">
<template v-slot:default="scope">
<div v-for="itemName in scope.row.tagNames" :key="itemName">
<div v-for="(itemName,index) in scope.row.tagId" :key="index">
<ms-tag type="success" effect="plain" :content="itemName"/>
</div>
</template>
@ -167,6 +167,11 @@
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
this.tableData.forEach(item => {
if (item.tagId && item.tagId.length > 0) {
item.tagId = JSON.parse(item.tagId);
}
})
this.loading = false;
});
},

View File

@ -80,24 +80,7 @@
<el-row>
<el-col :span="12">
<el-form-item label="Tag" prop="tagId">
<el-select v-model="currentScenario.tagId" size="small" class="ms-scenario-input" placeholder="Tag" :multiple="true">
<el-option
v-for="item in tags"
:key="item.id"
:label="item.name"
:value="item.id"/>
<el-button size="mini" type="primary" @click="openTagConfig" class="ms-scenario-button">
{{ $t('api_test.automation.create_tag') }}
</el-button>
<template v-slot:empty>
<div>
<el-button size="mini" type="primary" @click="openTagConfig" class="ms-scenario-button">
{{ $t('api_test.automation.create_tag') }}
</el-button>
</div>
</template>
</el-select>
<ms-input-tag :currentScenario="currentScenario" ref="tag"/>
</el-form-item>
</el-col>
<el-col :span="12">
@ -259,8 +242,6 @@
<!-- 环境 -->
<api-environment-config ref="environmentConfig" @close="environmentConfigClose"/>
<!--TAG-->
<ms-add-tag @refreshTags="refreshTags" ref="tag"/>
<!--执行组件-->
<ms-run :debug="true" :environment="currentEnvironmentId" :reportId="reportId" :run-data="debugData"
@runRefresh="runRefresh" ref="runTest"/>
@ -293,13 +274,14 @@
import MsApiCustomize from "./ApiCustomize";
import {getUUID, getCurrentProjectID} from "@/common/js/utils";
import ApiEnvironmentConfig from "../../definition/components/environment/ApiEnvironmentConfig";
import MsAddTag from "./AddTag";
import MsInputTag from "./MsInputTag";
import MsRun from "./DebugRun";
import MsImportApiScenario from "./ImportApiScenario";
import MsApiScenarioComponent from "./ApiScenarioComponent";
import MsApiReportDetail from "../report/ApiReportDetail";
import MsScenarioParameters from "./ScenarioParameters";
import ApiImport from "../../definition/components/import/ApiImport";
import InputTag from 'vue-input-tag'
export default {
name: "EditApiScenario",
@ -311,7 +293,7 @@
ApiEnvironmentConfig,
MsScenarioParameters,
MsApiReportDetail,
MsAddTag, MsRun,
MsInputTag, MsRun,
MsApiScenarioComponent,
MsImportApiScenario,
MsJsr233Processor,
@ -323,6 +305,7 @@
MsApiComponent,
MsApiCustomize,
ApiImport,
InputTag,
},
data() {
return {
@ -341,7 +324,6 @@
principal: [{required: true, message: this.$t('api_test.definition.request.responsible'), trigger: 'change'}],
},
environments: [],
tags: [],
currentEnvironmentId: "",
maintainerOptions: [],
value: API_STATUS[0].id,
@ -366,7 +348,8 @@
visibleRef: "",
enableCookieShare: false,
}
},
}
,
created() {
if (!this.currentScenario.apiScenarioModuleId) {
this.currentScenario.apiScenarioModuleId = "";
@ -374,11 +357,12 @@
this.projectId = getCurrentProjectID();
this.operatingElements = ELEMENTS.get("ALL");
this.getMaintainerOptions();
this.refreshTags();
this.getApiScenario();
this.getEnvironments();
},
watch: {},
}
,
watch: {}
,
methods: {
addComponent(type) {
switch (type) {
@ -423,7 +407,8 @@
}
this.sort();
this.reload();
},
}
,
nodeClick(e) {
if (e.referenced != 'REF' && e.referenced != 'Deleted') {
this.operatingElements = ELEMENTS.get(e.type);
@ -431,16 +416,19 @@
this.operatingElements = [];
}
this.selectedTreeNode = e;
},
}
,
showAll() {
this.operatingElements = ELEMENTS.get("ALL");
this.selectedTreeNode = undefined;
this.reload();
},
}
,
apiListImport() {
this.visibleRef = getUUID();
this.apiListVisible = true;
},
}
,
recursiveSorting(arr) {
for (let i in arr) {
arr[i].index = Number(i) + 1;
@ -448,7 +436,8 @@
this.recursiveSorting(arr[i].hashTree);
}
}
},
}
,
sort() {
for (let i in this.scenarioDefinition) {
this.scenarioDefinition[i].index = Number(i) + 1;
@ -456,7 +445,8 @@
this.recursiveSorting(this.scenarioDefinition[i].hashTree);
}
}
},
}
,
addCustomizeApi(request) {
this.customizeVisible = false;
request.enable === undefined ? request.enable = true : request.enable;
@ -468,7 +458,8 @@
this.customizeRequest = {};
this.sort();
this.reload();
},
}
,
addScenario(arr) {
if (arr && arr.length > 0) {
arr.forEach(item => {
@ -483,7 +474,8 @@
this.sort();
this.reload();
this.scenarioVisible = false;
},
}
,
setApiParameter(item, refType, referenced) {
let request = {};
if (Object.prototype.toString.call(item.request).indexOf("String") > 0) {
@ -512,7 +504,8 @@
} else {
this.scenarioDefinition.push(request);
}
},
}
,
pushApiOrCase(referenced) {
if (this.currentRow.cases.length === 0 && this.currentRow.apis.length === 0) {
this.$warning(this.$t('api_test.automation.reference_info'));
@ -529,13 +522,15 @@
this.currentRow.apis = [];
this.sort();
this.reload();
},
}
,
getMaintainerOptions() {
let workspaceId = localStorage.getItem(WORKSPACE_ID);
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
this.maintainerOptions = response.data;
});
},
}
,
openTagConfig() {
if (!this.projectId) {
this.$error(this.$t('api_test.select_project'));
@ -543,20 +538,6 @@
}
this.$refs.tag.open();
},
refreshTags() {
let obj = {projectId: this.projectId};
let tagIds = [];
this.$post('/api/tag/list', obj, response => {
this.tags = response.data;
this.tags.forEach(item => {
tagIds.push(item.id);
})
if (this.currentScenario.tagId != undefined && this.currentScenario.tagId.length > 0) {
this.currentScenario.tagId = this.currentScenario.tagId.filter(id => tagIds.indexOf(id) != -1);
}
});
},
remove(row, node) {
const parent = node.parent
const hashTree = parent.data.hashTree || parent.data;
@ -564,7 +545,8 @@
hashTree.splice(index, 1);
this.sort();
this.reload();
},
}
,
copyRow(row, node) {
const parent = node.parent
const hashTree = parent.data.hashTree || parent.data;
@ -574,13 +556,15 @@
hashTree.push(obj);
this.sort();
this.reload();
},
}
,
reload() {
this.loading = true
this.$nextTick(() => {
this.loading = false
})
},
}
,
runDebug() {
/*触发执行操作*/
if (!this.currentEnvironmentId) {
@ -593,7 +577,8 @@
environmentId: this.currentEnvironmentId, hashTree: this.scenarioDefinition
};
this.reportId = getUUID().substring(0, 8);
},
}
,
getEnvironments() {
if (this.projectId) {
this.$get('/api/environment/list/' + this.projectId, response => {
@ -603,17 +588,20 @@
});
});
}
},
}
,
openEnvironmentConfig() {
if (!this.projectId) {
this.$error(this.$t('api_test.select_project'));
return;
}
this.$refs.environmentConfig.open(this.projectId);
},
}
,
environmentConfigClose() {
this.getEnvironments();
},
}
,
allowDrop(draggingNode, dropNode, dropType) {
if (dropType != "inner") {
return true;
@ -622,21 +610,25 @@
return true;
}
return false;
},
}
,
allowDrag(draggingNode, dropNode, dropType) {
this.sort();
this.reload();
},
}
,
nodeExpand(data) {
if (data.resourceId) {
this.expandedNode.push(data.resourceId);
}
},
}
,
nodeCollapse(data) {
if (data.resourceId) {
this.expandedNode.splice(this.expandedNode.indexOf(data.resourceId), 1);
}
},
}
,
getPath(id) {
if (id === null) {
return null;
@ -645,7 +637,8 @@
return item.id === id ? item.path : "";
});
return path[0].path;
},
}
,
setFiles(item, bodyUploadFiles, obj) {
if (item.body) {
if (item.body.kvs) {
@ -683,7 +676,8 @@
});
}
}
},
}
,
recursiveFile(arr, bodyUploadFiles, obj) {
arr.forEach(item => {
this.setFiles(item, bodyUploadFiles, obj);
@ -691,7 +685,8 @@
this.recursiveFile(item.hashTree, bodyUploadFiles, obj);
}
});
},
}
,
getBodyUploadFiles(obj) {
let bodyUploadFiles = [];
obj.bodyUploadIds = [];
@ -702,7 +697,8 @@
}
})
return bodyUploadFiles;
},
}
,
editScenario() {
this.$refs['currentScenario'].validate((valid) => {
if (valid) {
@ -714,12 +710,15 @@
if (response.data) {
this.currentScenario.id = response.data.id;
}
this.currentScenario.tagId = JSON.parse(this.currentScenario.tagId);
if (this.currentScenario.tagId instanceof String) {
this.currentScenario.tagId = JSON.parse(this.currentScenario.tagId);
}
this.$emit('refresh');
})
}
})
},
}
,
getApiScenario() {
if (this.currentScenario.tagId != undefined && !(this.currentScenario.tagId instanceof Array)) {
this.currentScenario.tagId = JSON.parse(this.currentScenario.tagId);
@ -743,7 +742,8 @@
}
})
}
},
}
,
setParameter() {
this.currentScenario.stepTotal = this.scenarioDefinition.length;
this.currentScenario.projectId = getCurrentProjectID();
@ -762,18 +762,22 @@
this.currentScenario.apiScenarioModuleId = this.currentModule.id;
}
this.currentScenario.projectId = this.projectId;
},
}
,
runRefresh() {
this.debugVisible = true;
this.loading = false;
},
}
,
showScenarioParameters() {
this.$refs.scenarioParameters.open(this.currentScenario.variables);
},
}
,
addParameters(data) {
this.currentScenario.variables = data;
this.reload();
},
}
,
apiImport(importData) {
if (importData && importData.data) {
importData.data.forEach(item => {
@ -939,4 +943,9 @@
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
font-size: 13px;
}
/deep/ .el-form-item__content {
line-height: 100%;
}
</style>

View File

@ -0,0 +1,69 @@
<template>
<input-tag v-model="data"></input-tag>
</template>
<script>
import InputTag from 'vue-input-tag'
export default {
name: "MsInputTag",
components: {InputTag},
props: {currentScenario: {}},
created() {
if (!this.currentScenario.tagId) {
this.currentScenario.tagId = [];
}
console.log(this.currentScenario.tagId)
this.data = this.currentScenario.tagId;
},
data() {
return {
data: [],
}
},
watch: {
data() {
this.currentScenario.tagId = this.data;
}
},
methods: {}
}
</script>
<style scoped>
/deep/ .vue-input-tag-wrapper {
border-radius: 2px;
border: 1px solid #a5d24a;
color: #909399;
display: inline-block;
}
/deep/ .input-tag {
display: inline-block;
font-size: 12px;
min-width: auto;
border-width: 1px;
border-style: solid;
border-radius: 4px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
white-space: nowrap;
background-color: #fff;
border-color: #909399;
color: #909399;
width: auto;
height: 23px;
padding: 0 5px;
line-height: 19px;
}
/deep/ .remove {
color: #909399;
}
/deep/ .el-form-item__content {
line-height: 100%;
}
</style>