Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
wenyann 2020-06-05 15:30:11 +08:00
commit 1477e6961a
14 changed files with 142 additions and 81 deletions

View File

@ -17,6 +17,7 @@ import io.metersphere.controller.request.organization.AddOrgMemberRequest;
import io.metersphere.controller.request.organization.QueryOrgMemberRequest;
import io.metersphere.dto.UserDTO;
import io.metersphere.dto.UserRoleDTO;
import io.metersphere.i18n.Translator;
import io.metersphere.service.OrganizationService;
import io.metersphere.service.UserService;
import io.metersphere.service.WorkspaceService;
@ -206,7 +207,7 @@ public class UserController {
workspaceService.checkWorkspaceOwner(workspaceId);
String currentUserId = SessionUtils.getUser().getId();
if (StringUtils.equals(userId, currentUserId)) {
MSException.throwException("Insufficient permissions!");
MSException.throwException(Translator.get("cannot_remove_current"));
}
userService.deleteMember(workspaceId, userId);
}
@ -230,7 +231,7 @@ public class UserController {
organizationService.checkOrgOwner(organizationId);
String currentUserId = SessionUtils.getUser().getId();
if (StringUtils.equals(userId, currentUserId)) {
MSException.throwException("Insufficient permissions!");
MSException.throwException(Translator.get("cannot_remove_current"));
}
userService.delOrganizationMember(organizationId, userId);
}

View File

@ -141,8 +141,8 @@ public class UserService {
UserDTO userDTO = new UserDTO();
BeanUtils.copyProperties(user, userDTO);
UserRoleDTO userRole = getUserRole(userId);
userDTO.setUserRoles(userRole.getUserRoles());
userDTO.setRoles(userRole.getRoles());
userDTO.setUserRoles(Optional.ofNullable(userRole.getUserRoles()).orElse(new ArrayList<>()));
userDTO.setRoles(Optional.ofNullable(userRole.getRoles()).orElse(new ArrayList<>()));
return userDTO;
}

View File

@ -21,10 +21,12 @@ import io.metersphere.i18n.Translator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
@ -135,6 +137,7 @@ public class WorkspaceService {
public void checkWorkspaceOwner(String workspaceId) {
checkWorkspaceIsExist(workspaceId);
int size = 0;
WorkspaceExample example = new WorkspaceExample();
SessionUser sessionUser = SessionUtils.getUser();
UserDTO user = userService.getUserDTO(sessionUser.getId());
@ -142,15 +145,18 @@ public class WorkspaceService {
.filter(ur -> RoleConstants.ORG_ADMIN.equals(ur.getRoleId()))
.map(UserRole::getSourceId)
.collect(Collectors.toList());
example.createCriteria()
.andOrganizationIdIn(orgIds)
.andIdEqualTo(workspaceId);
if (!CollectionUtils.isEmpty(orgIds)) {
example.createCriteria()
.andOrganizationIdIn(orgIds)
.andIdEqualTo(workspaceId);
size = (int) workspaceMapper.countByExample(example);
}
List<String> wsIds = user.getUserRoles().stream()
.filter(ur -> RoleConstants.TEST_MANAGER.equals(ur.getRoleId()))
.map(UserRole::getSourceId)
.collect(Collectors.toList());
boolean contains = wsIds.contains(workspaceId);
if (workspaceMapper.countByExample(example) == 0 && !contains) {
if (size == 0 && !contains) {
MSException.throwException(Translator.get("workspace_does_not_belong_to_user"));
}
}

View File

@ -15,6 +15,15 @@ user_id_already_exists=User ID already exists
password_modification_failed=The old password is wrong. Please re-enter it
cannot_delete_current_user=Cannot delete the user currently logged in
user_already_exists=The user already exists in the current member list
cannot_remove_current=Unable to remove the currently logged in user
password_is_incorrect=Incorrect password
user_not_exist=user does not exist
user_has_been_disabled=the user has been disabled.
excessive_attempts=Excessive attempts
user_locked=the user has been locked.
user_expires=user expires.
not_authorized=not authorized.
login_fail=Login fail
#load test
edit_load_test_not_found=Cannot edit test, test not found=
run_load_test_not_found=Cannot run test, test not found=
@ -97,11 +106,3 @@ test_case_report_template_repeat=The workspace has the same name template
plan_name_already_exists=Test plan name already exists
test_case_already_exists_excel=There are duplicate test cases in the import file
api_test_name_already_exists=Test name already exists
password_is_incorrect=Incorrect password
user_not_exist=user does not exist
user_has_been_disabled=the user has been disabled.
excessive_attempts=Excessive attempts
user_locked=the user has been locked.
user_expires=user expires.
not_authorized=not authorized.
login_fail=Login fail

View File

@ -15,7 +15,15 @@ password_modification_failed=旧密码输入错误,请重新输入
cannot_delete_current_user=无法删除当前登录用户
connection_failed=连接失败
user_already_exists=该用户已存在于当前成员列表中
cannot_remove_current=无法移除当前登录用户
login_fail=登陆失败
password_is_incorrect=密码不正确
user_not_exist=用户不存在:
user_has_been_disabled=用户已被禁用
excessive_attempts=操作频繁
user_locked=用户被锁定
user_expires=用户过期
not_authorized=未经授权
#load test
edit_load_test_not_found=无法编辑测试,未找到测试:
run_load_test_not_found=无法运行测试,未找到测试:
@ -98,14 +106,7 @@ test_case_report_template_repeat=同一工作空间下不能存在同名模版
plan_name_already_exists=测试计划名称已存在
test_case_already_exists_excel=导入文件中存在重复用例
api_test_name_already_exists=测试名称已经存在
login_fail=登陆失败
password_is_incorrect=密码不正确
user_not_exist=用户不存在:
user_has_been_disabled=用户已被禁用
excessive_attempts=操作频繁
user_locked=用户被锁定
user_expires=用户过期
not_authorized=未经授权

View File

@ -15,6 +15,15 @@ user_id_already_exists=用戶id已存在
password_modification_failed=舊密碼輸入錯誤,請重新輸入
cannot_delete_current_user=無法刪除當前登錄用戶
user_already_exists=該用戶已存在於當前成員列表中
cannot_remove_current=無法移除當前登錄用戶
password_is_incorrect=密碼不正確
user_not_exist=用戶不存在:
user_has_been_disabled=用戶已被禁用
excessive_attempts=操作頻繁
user_locked=用戶被鎖定
user_expires=用戶過期
not_authorized=未經授權。
login_fail=登入失敗
#load test
edit_load_test_not_found=無法編輯測試,未找到測試:
run_load_test_not_found=無法運行測試,未找到測試:
@ -97,11 +106,4 @@ test_case_report_template_repeat=同壹工作空間下不能存在同名模版
plan_name_already_exists=測試計劃名稱已存在
test_case_already_exists_excel=導入文件中存在重復用例
api_test_name_already_exists=測試名稱已經存在
password_is_incorrect=密碼不正確
user_not_exist=用戶不存在:
user_has_been_disabled.=用戶已被禁用
excessive_attempts=操作頻繁
user_locked=用戶被鎖定
user_expires=用戶過期
not_authorized=未經授權。
login_fail=登入失敗

View File

@ -5,27 +5,27 @@
<el-container class="test-container" v-loading="result.loading">
<el-header>
<el-row type="flex" align="middle">
<el-input class="test-name" v-model="test.name" maxlength="60" :placeholder="$t('api_test.input_name')"
<el-input :disabled="isReadOnly" class="test-name" v-model="test.name" maxlength="60" :placeholder="$t('api_test.input_name')"
show-word-limit>
<el-select class="test-project" v-model="test.projectId" slot="prepend"
<el-select :disabled="isReadOnly" class="test-project" v-model="test.projectId" slot="prepend"
:placeholder="$t('api_test.select_project')">
<el-option v-for="project in projects" :key="project.id" :label="project.name" :value="project.id"/>
</el-select>
</el-input>
<el-button type="primary" plain :disabled="isDisabled" @click="saveTest">
<el-button type="primary" plain :disabled="isDisabled || isReadOnly" @click="saveTest">
{{$t('commons.save')}}
</el-button>
<el-button type="primary" plain v-if="!isShowRun" :disabled="isDisabled" @click="saveRunTest">
<el-button type="primary" plain v-if="!isShowRun" :disabled="isDisabled || isReadOnly" @click="saveRunTest">
{{$t('load_test.save_and_run')}}
</el-button>
<el-button type="primary" plain v-if="isShowRun" @click="runTest">
<el-button :disabled="isReadOnly" type="primary" plain v-if="isShowRun" @click="runTest">
{{$t('api_test.run')}}
</el-button>
<el-button type="warning" plain @click="cancel">{{$t('commons.cancel')}}</el-button>
<el-button :disabled="isReadOnly" type="warning" plain @click="cancel">{{$t('commons.cancel')}}</el-button>
<el-dropdown trigger="click" @command="handleCommand">
<el-button class="el-dropdown-link more" icon="el-icon-more" plain/>
@ -33,7 +33,7 @@
<el-dropdown-item command="report" :disabled="test.status !== 'Completed'">
{{$t('api_report.title')}}
</el-dropdown-item>
<el-dropdown-item command="performance" :disabled="create">
<el-dropdown-item command="performance" :disabled="create || isReadOnly">
{{$t('api_test.create_performance_test')}}
</el-dropdown-item>
</el-dropdown-menu>
@ -42,7 +42,7 @@
<ms-api-report-dialog :test-id="id" ref="reportDialog"/>
</el-row>
</el-header>
<ms-api-scenario-config :scenarios="test.scenarioDefinition" ref="config"/>
<ms-api-scenario-config :is-read-only="isReadOnly" :scenarios="test.scenarioDefinition" ref="config"/>
</el-container>
</el-card>
</div>
@ -54,6 +54,7 @@
import {Test} from "./model/ScenarioModel"
import MsApiReportStatus from "../report/ApiReportStatus";
import MsApiReportDialog from "./ApiReportDialog";
import {checkoutTestManagerOrTestUser} from "../../../../common/js/utils";
export default {
name: "MsApiTestConfig",
@ -69,7 +70,8 @@
result: {},
projects: [],
change: false,
test: new Test()
test: new Test(),
isReadOnly: false
}
},
@ -86,6 +88,10 @@
methods: {
init() {
let projectId;
this.isReadOnly = false;
if (!checkoutTestManagerOrTestUser()) {
this.isReadOnly = true;
}
if (this.id) {
this.create = false;
this.getTest(this.id);

View File

@ -7,7 +7,7 @@
:title="$t('commons.test')"
@create="create" :createTip="$t('load_test.create')"/>
</template>
<el-table :data="tableData" class="table-content" @sort-change="sort" @row-click="handleEdit"
<el-table :data="tableData" class="table-content" @sort-change="sort" @row-click="handleView"
@filter-change="filter">
<el-table-column prop="name" :label="$t('commons.name')" width="250" show-overflow-tooltip>
</el-table-column>
@ -124,6 +124,11 @@
path: '/api/test/edit?id=' + test.id,
})
},
handleView(test) {
this.$router.push({
path: '/api/test/view?id=' + test.id,
})
},
handleDelete(test) {
this.$alert(this.$t('load_test.delete_confirm') + test.name + "", '', {
confirmButtonText: this.$t('commons.confirm'),

View File

@ -1,6 +1,6 @@
<template>
<ms-tip-button
:disabled="disabled"
:disabled="disabled || isReadOnly"
@click="exec"
@clickStop="clickStop"
:type="type"
@ -16,6 +16,11 @@
export default {
name: "MsTableOperatorButton",
components: {MsTipButton, MsTableButton},
data() {
return {
isReadOnly: false
}
},
props: {
icon: {
type: String,
@ -39,7 +44,7 @@
},
mounted() {
if (this.isTesterPermission && !hasRoles(ROLE_TEST_USER, ROLE_TEST_MANAGER)) {
this.disabled = true;
this.isReadOnly = true;
}
},
methods: {

View File

@ -14,10 +14,10 @@
</el-breadcrumb>
</el-row>
<el-row class="ms-report-view-btns">
<el-button type="primary" plain size="mini">{{$t('report.test_stop_now')}}</el-button>
<el-button type="success" plain size="mini">{{$t('report.test_execute_again')}}</el-button>
<el-button type="info" plain size="mini">{{$t('report.export')}}</el-button>
<el-button type="warning" plain size="mini">{{$t('report.compare')}}</el-button>
<el-button :disabled="isReadOnly" type="primary" plain size="mini">{{$t('report.test_stop_now')}}</el-button>
<el-button :disabled="isReadOnly" type="success" plain size="mini">{{$t('report.test_execute_again')}}</el-button>
<el-button :disabled="isReadOnly" type="info" plain size="mini">{{$t('report.export')}}</el-button>
<el-button :disabled="isReadOnly" type="warning" plain size="mini">{{$t('report.compare')}}</el-button>
</el-row>
</el-col>
<el-col :span="8">
@ -63,6 +63,7 @@
import MsReportTestOverview from './components/TestOverview';
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import {checkoutTestManagerOrTestUser} from "../../../../common/js/utils";
export default {
name: "PerformanceReportView",
@ -90,7 +91,8 @@
minutes: '0',
seconds: '0',
title: 'Logging',
report: {}
report: {},
isReadOnly: false
}
},
methods: {
@ -152,6 +154,10 @@
}
},
created() {
this.isReadOnly = false;
if (!checkoutTestManagerOrTestUser()) {
this.isReadOnly = true;
}
this.reportId = this.$route.path.split('/')[4];
this.result = this.$get("/performance/report/" + this.reportId, res => {
let data = res.data;
@ -169,6 +175,10 @@
watch: {
'$route'(to) {
if (to.name === "perReportView") {
this.isReadOnly = false;
if (!checkoutTestManagerOrTestUser()) {
this.isReadOnly = true;
}
let reportId = to.path.split('/')[4];
this.reportId = reportId;
if (reportId) {

View File

@ -4,11 +4,11 @@
<el-card v-loading="result.loading">
<el-row>
<el-col :span="10">
<el-input :placeholder="$t('load_test.input_name')" v-model="testPlan.name" class="input-with-select"
<el-input :disabled="isReadOnly" :placeholder="$t('load_test.input_name')" v-model="testPlan.name" class="input-with-select"
maxlength="30"
>
<template v-slot:prepend>
<el-select v-model="testPlan.projectId" :placeholder="$t('load_test.select_project')">
<el-select :disabled="isReadOnly" v-model="testPlan.projectId" :placeholder="$t('load_test.select_project')">
<el-option
v-for="item in projects"
:key="item.id"
@ -20,22 +20,22 @@
</el-input>
</el-col>
<el-col :span="12" :offset="2">
<el-button type="primary" plain @click="save">{{$t('commons.save')}}</el-button>
<el-button type="primary" plain @click="saveAndRun">{{$t('load_test.save_and_run')}}</el-button>
<el-button type="warning" plain @click="cancel">{{$t('commons.cancel')}}</el-button>
<el-button :disabled="isReadOnly" type="primary" plain @click="save">{{$t('commons.save')}}</el-button>
<el-button :disabled="isReadOnly" type="primary" plain @click="saveAndRun">{{$t('load_test.save_and_run')}}</el-button>
<el-button :disabled="isReadOnly" type="warning" plain @click="cancel">{{$t('commons.cancel')}}</el-button>
</el-col>
</el-row>
<el-tabs class="testplan-config" v-model="active" type="border-card" :stretch="true">
<el-tab-pane :label="$t('load_test.basic_config')">
<performance-basic-config :test-plan="testPlan" ref="basicConfig"/>
<performance-basic-config :is-read-only="isReadOnly" :test-plan="testPlan" ref="basicConfig"/>
</el-tab-pane>
<el-tab-pane :label="$t('load_test.pressure_config')">
<performance-pressure-config :test-plan="testPlan" :test-id="testId" ref="pressureConfig" @changeActive="changeTabActive"/>
<performance-pressure-config :is-read-only="isReadOnly" :test-plan="testPlan" :test-id="testId" ref="pressureConfig" @changeActive="changeTabActive"/>
</el-tab-pane>
<el-tab-pane :label="$t('load_test.advanced_config')" class="advanced-config">
<performance-advanced-config :test-id="testId" ref="advancedConfig"/>
<performance-advanced-config :read-only="isReadOnly" :test-id="testId" ref="advancedConfig"/>
</el-tab-pane>
</el-tabs>
</el-card>
@ -49,6 +49,7 @@
import PerformanceAdvancedConfig from "./components/PerformanceAdvancedConfig";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import {checkoutTestManagerOrTestUser} from "../../../../common/js/utils";
export default {
name: "EditPerformanceTestPlan",
@ -70,6 +71,7 @@
projects: [],
active: '0',
testId: '',
isReadOnly: false,
tabs: [{
title: this.$t('load_test.basic_config'),
id: '0',
@ -97,6 +99,11 @@
return;
}
this.isReadOnly = false;
if (!checkoutTestManagerOrTestUser()) {
this.isReadOnly = true;
}
let testId = to.params.testId;
if (testId) {
this.testId = testId;
@ -111,6 +118,10 @@
},
created() {
let testId = this.$route.params.testId;
this.isReadOnly = false;
if (!checkoutTestManagerOrTestUser()) {
this.isReadOnly = true;
}
if (testId) {
this.testId = testId;
this.result = this.$get('/performance/get/' + testId, response => {

View File

@ -61,7 +61,7 @@
import MsContainer from "../../common/components/MsContainer";
import MsAsideContainer from "../../common/components/MsAsideContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import {hasRoles} from "../../../../common/js/utils";
import {checkoutTestManagerOrTestUser, hasRoles} from "../../../../common/js/utils";
export default {
name: "TestCase",
@ -86,32 +86,33 @@
}
},
mounted() {
this.getProjects();
this.refresh();
if (this.$route.path.indexOf("/track/case/edit") >= 0 || this.$route.path.indexOf("/track/case/create") >= 0){
this.openRecentTestCaseEditDialog();
this.$router.push('/track/case/all');
} else if (this.$route.params.projectId){
this.getProjectById(this.$route.params.projectId)
}
this.init(this.$route);
},
watch: {
'$route'(to, from) {
let path = to.path;
if (path.indexOf("/track/case/edit") >= 0 || path.indexOf("/track/case/create") >= 0){
this.openRecentTestCaseEditDialog();
this.$router.push('/track/case/all');
this.getProjects();
} else if (to.params.projectId){
this.getProjectById(to.params.projectId);
this.getProjects();
}
this.init(to);
},
currentProject() {
this.refresh();
}
},
methods: {
init(route) {
let path = route.path;
if (path.indexOf("/track/case/edit") >= 0 || path.indexOf("/track/case/create") >= 0){
this.getProjects();
this.testCaseReadOnly = false;
if (!checkoutTestManagerOrTestUser()) {
this.testCaseReadOnly = true;
}
let caseId = this.$route.params.caseId;
this.openRecentTestCaseEditDialog(caseId);
this.$router.push('/track/case/all');
} else if (route.params.projectId){
this.getProjects();
this.getProjectById(route.params.projectId);
}
},
getProjects() {
this.$get("/project/listAll", (response) => {
this.projects = response.data;
@ -187,18 +188,15 @@
this.$refs.testCaseList.initTableData();
this.getNodeTree();
},
openRecentTestCaseEditDialog() {
let caseId = this.$route.params.caseId;
openRecentTestCaseEditDialog(caseId) {
if (caseId) {
this.getProjectByCaseId(caseId);
this.$get('/test/case/get/' + caseId, response => {
if (response.data) {
this.testCaseReadOnly = false;
this.$refs.testCaseEditDialog.open(response.data);
}
});
} else {
this.testCaseReadOnly = false;
this.$refs.testCaseEditDialog.open();
}
},

View File

@ -151,7 +151,14 @@
import NodeBreadcrumb from '../../../common/NodeBreadcrumb';
import {ROLE_TEST_MANAGER, ROLE_TEST_USER, TokenKey} from '../../../../../../common/js/constants';
import {_filter, _sort, hasRoles, humpToLine, tableFilter} from '../../../../../../common/js/utils';
import {
_filter,
_sort,
checkoutTestManagerOrTestUser,
hasRoles,
humpToLine,
tableFilter
} from '../../../../../../common/js/utils';
import PriorityTableItem from "../../../common/tableItems/planview/PriorityTableItem";
import StatusTableItem from "../../../common/tableItems/planview/StatusTableItem";
import TypeTableItem from "../../../common/tableItems/planview/TypeTableItem";
@ -272,6 +279,9 @@
},
handleEdit(testCase, index) {
this.isReadOnly = false;
if (!checkoutTestManagerOrTestUser()) {
this.isReadOnly = true;
}
this.$refs.testPlanTestCaseEdit.openTestCaseEdit(testCase);
},
handleDelete(testCase) {

View File

@ -37,6 +37,11 @@ export function checkoutCurrentWorkspace() {
return user.userRoles.filter(ur => hasRoles(ROLE_TEST_MANAGER, ROLE_TEST_USER, ROLE_TEST_VIEWER) && user.lastWorkspaceId === ur.sourceId).length > 0;
}
export function checkoutTestManagerOrTestUser() {
let user = getCurrentUser();
return user.userRoles.filter(ur => hasRoles(ROLE_TEST_MANAGER, ROLE_TEST_USER) && user.lastWorkspaceId === ur.sourceId).length > 0;
}
export function getCurrentOrganizationId() {
let user = getCurrentUser();
return user.lastOrganizationId;