refactor(接口测试): 报告分享增加权限位

This commit is contained in:
wxg0103 2024-03-29 16:57:46 +08:00 committed by Craftsman
parent b8364cc042
commit 81883e5934
15 changed files with 186 additions and 12 deletions

View File

@ -145,6 +145,7 @@ INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_REPORT:READ');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_REPORT:READ+UPDATE');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_REPORT:READ+DELETE');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_REPORT:READ+SHARE');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEFINITION_DOC:READ');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEFINITION_DOC:READ+SHARE');
@ -237,7 +238,7 @@ INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_REPORT:READ');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_REPORT:READ+UPDATE');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_REPORT:READ+DELETE');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_REPORT:READ+SHARE');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'FUNCTIONAL_CASE:READ');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'FUNCTIONAL_CASE:READ+ADD');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'FUNCTIONAL_CASE:READ+UPDATE');

View File

@ -286,6 +286,7 @@ public class PermissionConstants {
public static final String PROJECT_API_REPORT_READ = "PROJECT_API_REPORT:READ";
public static final String PROJECT_API_REPORT_UPDATE = "PROJECT_API_REPORT:READ+UPDATE";
public static final String PROJECT_API_REPORT_DELETE = "PROJECT_API_REPORT:READ+DELETE";
public static final String PROJECT_API_REPORT_SHARE = "PROJECT_API_REPORT:READ+SHARE";
/*------ end: API_REPORT ------*/
//个人中心

View File

@ -4,10 +4,13 @@ import io.metersphere.api.dto.share.ApiReportShareDTO;
import io.metersphere.api.dto.share.ApiReportShareRequest;
import io.metersphere.api.dto.share.ShareInfoDTO;
import io.metersphere.api.service.ApiReportShareService;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.system.utils.SessionUtils;
import io.metersphere.validation.groups.Created;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@ -22,14 +25,23 @@ public class ApiReportShareController {
private ApiReportShareService apiReportShareService;
@PostMapping("/gen")
@Operation(summary = "接口测试-接口报告-生成分享链接")
@RequiresPermissions(PermissionConstants.PROJECT_API_REPORT_SHARE)
public ShareInfoDTO generateShareInfo(@Validated(Created.class) @RequestBody ApiReportShareRequest request) {
return apiReportShareService.gen(request, Objects.requireNonNull(SessionUtils.getUser()));
}
@GetMapping("/get/{id}")
@Operation(summary = "接口测试-接口报告-获取分享链接")
public ApiReportShareDTO get(@PathVariable String id) {
return apiReportShareService.get(id);
}
@GetMapping("/get-share-time/{id}")
@Operation(summary = "接口测试-接口报告-获取分享链接的有效时间")
public String getShareTime(@PathVariable String id) {
return apiReportShareService.getShareTime(id);
}
}

View File

@ -19,4 +19,7 @@ public class ApiReportShareDTO {
@Schema(description = "分享扩展数据 资源的id" ,requiredMode = Schema.RequiredMode.REQUIRED)
private String reportId;
@Schema(description = "分享链接是否被删")
private Boolean deleted;
}

View File

@ -127,6 +127,28 @@ public class ApiReportShareService {
ApiReportShareDTO dto = new ApiReportShareDTO();
BeanUtils.copyBean(dto, shareInfo);
dto.setReportId(new String(shareInfo.getCustomData()));
//检查id是否存在
dto.setDeleted(false);
ApiReport apiReport = apiReportMapper.selectByPrimaryKey(dto.getReportId());
if (apiReport != null && BooleanUtils.isTrue(apiReport.getDeleted())) {
dto.setDeleted(true);
} else {
ApiScenarioReport result = apiScenarioReportMapper.selectByPrimaryKey(dto.getReportId());
if (result != null && BooleanUtils.isTrue(result.getDeleted())) {
dto.setDeleted(true);
}
}
return dto;
}
public String getShareTime(String projectId) {
ProjectApplicationExample example = new ProjectApplicationExample();
example.createCriteria().andProjectIdEqualTo(projectId).andTypeEqualTo(ShareInfoType.API_SHARE_REPORT.name());
List<ProjectApplication> projectApplications = projectApplicationMapper.selectByExample(example);
if (CollectionUtils.isEmpty(projectApplications)) {
return "1D";
} else {
return projectApplications.getFirst().getTypeValue();
}
}
}

View File

@ -153,6 +153,10 @@
},
{
"id": "PROJECT_API_REPORT:READ+DELETE"
},
{
"id": "PROJECT_API_REPORT:READ+SHARE",
"name": "permission.api_doc.share"
}
]
}

View File

@ -463,6 +463,14 @@ public class ApiReportControllerTests extends BaseTest {
this.requestGetWithOk("/api/report/share/get/" + shareId)
.andReturn();
ApiReport apiReport = apiReportMapper.selectByPrimaryKey("test-report-id");
apiReport.setDeleted(true);
apiReportMapper.updateByPrimaryKey(apiReport);
this.requestGetWithOk("/api/report/share/get/" + shareId)
.andReturn();
apiReport.setDeleted(false);
apiReportMapper.updateByPrimaryKey(apiReport);
mockMvc.perform(getRequestBuilder("/api/report/share/get/" + "test"))
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(status().is5xxServerError());

View File

@ -446,6 +446,14 @@ public class ApiScenarioReportControllerTests extends BaseTest {
this.requestGetWithOk("/api/report/share/get/" + shareId)
.andReturn();
ApiScenarioReport scenarioReport = apiScenarioReportMapper.selectByPrimaryKey("test-scenario-report-id");
scenarioReport.setDeleted(true);
apiScenarioReportMapper.updateByPrimaryKeySelective(scenarioReport);
this.requestGetWithOk("/api/report/share/get/" + shareId)
.andReturn();
scenarioReport.setDeleted(false);
apiScenarioReportMapper.updateByPrimaryKeySelective(scenarioReport);
mockMvc.perform(getRequestBuilder("/api/report/share/get/" + "test"))
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(status().is5xxServerError());
@ -475,6 +483,8 @@ public class ApiScenarioReportControllerTests extends BaseTest {
Assertions.assertNotNull(shareInfoDTO.getId());
shareId = shareInfoDTO.getId();
this.requestGetWithOk("/api/report/share/get-share-time/" + DEFAULT_PROJECT_ID)
.andReturn();
ProjectApplicationExample projectApplicationExample = new ProjectApplicationExample();
projectApplicationExample.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andTypeEqualTo(ShareInfoType.API_SHARE_REPORT.name());
List<ProjectApplication> projectApplications = projectApplicationMapper.selectByExample(projectApplicationExample);
@ -485,6 +495,8 @@ public class ApiScenarioReportControllerTests extends BaseTest {
projectApplication.setTypeValue("1D");
projectApplicationMapper.insert(projectApplication);
}
this.requestGetWithOk("/api/report/share/get-share-time/" + DEFAULT_PROJECT_ID)
.andReturn();
mvcResult1 = this.requestGetWithOk(BASIC + "/share/" + shareId + "/" + "test-scenario-report-id")
.andReturn();

View File

@ -101,4 +101,8 @@ export function getShareReportInfo(shareId: string) {
return MSR.get<ReportDetail>({ url: `${reportUrl.getShareReportInfoUrl}/${shareId}` });
}
// 获取分享时间
export function getShareTime(projectId: string) {
return MSR.get<string>({ url: `${reportUrl.getShareTimeUrl}/${projectId}` });
}
export default {};

View File

@ -44,3 +44,4 @@ export const reportCaseShareUrl = '/api/report/case/share';
// 获取分享id
export const getShareIdUrl = '/api/report/share/gen';
export const getShareReportInfoUrl = '/api/report/share/get';
export const getShareTimeUrl = '/api/report/share/get-share-time';

View File

@ -9,10 +9,24 @@
show-full-screen
>
<template #tbutton>
<MsButton type="icon" status="secondary" class="mr-4 !rounded-[var(--border-radius-small)]" @click="shareHandler">
<MsIcon type="icon-icon_share1" class="mr-2 font-[16px]" />
{{ t('common.share') }}
</MsButton>
<a-dropdown position="br" @select="shareHandler">
<MsButton
v-permission="['PROJECT_API_REPORT:READ+SHARE']"
type="icon"
status="secondary"
class="mr-4 !rounded-[var(--border-radius-small)]"
@click="shareHandler"
>
<MsIcon type="icon-icon_share1" class="mr-2 font-[16px]" />
{{ t('common.share') }}
</MsButton>
<template #content>
<a-doption>
<span>{{ t('report.detail.api.copyLink') }}</span
><span>{{ t('report.detail.api.copyLinkTimeEnd', { time: shareTime }) }}</span>
</a-doption>
</template>
</a-dropdown>
</template>
<CaseReportCom :detail-info="reportStepDetail" />
</MsDrawer>
@ -26,7 +40,7 @@
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
import CaseReportCom from '@/views/api-test/report/component/caseReportCom.vue';
import { getShareInfo, reportCaseDetail } from '@/api/modules/api-test/report';
import { getShareInfo, getShareTime, reportCaseDetail } from '@/api/modules/api-test/report';
import { useI18n } from '@/hooks/useI18n';
import { useAppStore } from '@/store';
@ -136,4 +150,34 @@
console.log(error);
}
}
const shareTime = ref<string>('');
async function getTime() {
const res = await getShareTime(appStore.currentProjectId);
const match = res.match(/^(\d+)([MYHD])$/);
if (match) {
const value = parseInt(match[1], 10); //
const type = match[2]; //
switch (type) {
case 'M':
shareTime.value = value + t('msTimeSelector.month');
break;
case 'Y':
shareTime.value = value + t('msTimeSelector.year');
break;
case 'H':
shareTime.value = value + t('msTimeSelector.hour');
break;
case 'D':
shareTime.value = value + t('msTimeSelector.day');
break;
default:
shareTime.value = 24 + t('msTimeSelector.hour');
break;
}
}
}
onMounted(() => {
getTime();
});
</script>

View File

@ -19,6 +19,7 @@
<div class="rightButtons flex items-center">
<a-dropdown position="br" @select="shareHandler">
<MsButton
v-permission="['PROJECT_API_REPORT:READ+SHARE']"
type="icon"
status="secondary"
class="mr-4 !rounded-[var(--border-radius-small)]"
@ -32,7 +33,7 @@
<template #content>
<a-doption>
<span>{{ t('report.detail.api.copyLink') }}</span
><span>{{ t('report.detail.api.copyLinkTimeEnd') }}</span>
><span>{{ t('report.detail.api.copyLinkTimeEnd', { time: shareTime }) }}</span>
</a-doption>
</template>
</a-dropdown>
@ -65,7 +66,7 @@
import MsDetailDrawer from '@/components/business/ms-detail-drawer/index.vue';
import CaseReportCom from './caseReportCom.vue';
import { getShareInfo, reportCaseDetail } from '@/api/modules/api-test/report';
import { getShareInfo, getShareTime, reportCaseDetail } from '@/api/modules/api-test/report';
import { useI18n } from '@/hooks/useI18n';
import { useAppStore } from '@/store';
@ -179,6 +180,35 @@
console.log(error);
}
}
const shareTime = ref<string>('');
async function getTime() {
const res = await getShareTime(appStore.currentProjectId);
const match = res.match(/^(\d+)([MYHD])$/);
if (match) {
const value = parseInt(match[1], 10); //
const type = match[2]; //
switch (type) {
case 'M':
shareTime.value = value + t('msTimeSelector.month');
break;
case 'Y':
shareTime.value = value + t('msTimeSelector.year');
break;
case 'H':
shareTime.value = value + t('msTimeSelector.hour');
break;
case 'D':
shareTime.value = value + t('msTimeSelector.day');
break;
default:
shareTime.value = 24 + t('msTimeSelector.hour');
break;
}
}
}
onMounted(() => {
getTime();
});
//
function loadedReport(detail: ReportDetail) {

View File

@ -19,6 +19,7 @@
<div class="rightButtons flex items-center">
<a-dropdown position="br" @select="shareHandler">
<MsButton
v-permission="['PROJECT_API_REPORT:READ+SHARE']"
type="icon"
status="secondary"
class="mr-4 !rounded-[var(--border-radius-small)]"
@ -32,7 +33,7 @@
<template #content>
<a-doption>
<span>{{ t('report.detail.api.copyLink') }}</span
><span>{{ t('report.detail.api.copyLinkTimeEnd') }}</span>
><span>{{ t('report.detail.api.copyLinkTimeEnd', { time: shareTime }) }}</span>
</a-doption>
</template>
</a-dropdown>
@ -66,13 +67,15 @@
import MsDetailDrawer from '@/components/business/ms-detail-drawer/index.vue';
import ScenarioCom from './scenarioCom.vue';
import { getShareInfo, reportScenarioDetail } from '@/api/modules/api-test/report';
import { getShareInfo, getShareTime, reportScenarioDetail } from '@/api/modules/api-test/report';
import { useI18n } from '@/hooks/useI18n';
import { useAppStore } from '@/store';
import type { ReportDetail } from '@/models/apiTest/report';
import { RouteEnum } from '@/enums/routeEnum';
import * as constants from 'constants';
const appStore = useAppStore();
const { t } = useI18n();
@ -186,6 +189,35 @@
console.log(error);
}
}
const shareTime = ref<string>('');
async function getTime() {
const res = await getShareTime(appStore.currentProjectId);
const match = res.match(/^(\d+)([MYHD])$/);
if (match) {
const value = parseInt(match[1], 10); //
const type = match[2]; //
switch (type) {
case 'M':
shareTime.value = value + t('msTimeSelector.month');
break;
case 'Y':
shareTime.value = value + t('msTimeSelector.year');
break;
case 'H':
shareTime.value = value + t('msTimeSelector.hour');
break;
case 'D':
shareTime.value = value + t('msTimeSelector.day');
break;
default:
shareTime.value = 24 + t('msTimeSelector.hour');
break;
}
}
}
onMounted(() => {
getTime();
});
/**
* 导出

View File

@ -76,7 +76,7 @@ export default {
'report.detail.api.reportCreator': 'Report creator',
'report.detail.api.runMode': 'Run mode',
'report.detail.api.copyLink': 'Copy link',
'report.detail.api.copyLinkTimeEnd': '(Valid 24 hours)',
'report.detail.api.copyLinkTimeEnd': '(Valid {time})',
'report.detail.api.defaultEnv': 'Default environment',
'report.detail.api.caseSaveEnv': 'Case saved environment',
'report.detail.api.scenarioSavedEnv': 'Scenario saved environment',

View File

@ -75,7 +75,7 @@ export default {
'report.detail.api.reportCreator': '报告创建人',
'report.detail.api.runMode': '运行模式',
'report.detail.api.copyLink': '复制链接',
'report.detail.api.copyLinkTimeEnd': '(24小时有效)',
'report.detail.api.copyLinkTimeEnd': '({time}有效)',
'report.detail.api.defaultEnv': '默认环境',
'report.detail.api.caseSaveEnv': '用例保存的环境',
'report.detail.api.scenarioSavedEnv': '场景保存的环境',