diff --git a/backend/framework/sdk/src/main/resources/i18n/project.properties b/backend/framework/sdk/src/main/resources/i18n/project.properties index 05f7e70a11..72b31baa5c 100644 --- a/backend/framework/sdk/src/main/resources/i18n/project.properties +++ b/backend/framework/sdk/src/main/resources/i18n/project.properties @@ -444,6 +444,7 @@ file.log.previous=之前 file.log.upload=上传 file.log.repository.add=添加了存储库文件 file.log.re-upload=重新上传 +file.log.pull=拉取了文件 file.name.cannot.be.empty=文件名称不能为空 #file management over diff --git a/backend/framework/sdk/src/main/resources/i18n/project_en_US.properties b/backend/framework/sdk/src/main/resources/i18n/project_en_US.properties index 20153d1456..8f6b005eba 100644 --- a/backend/framework/sdk/src/main/resources/i18n/project_en_US.properties +++ b/backend/framework/sdk/src/main/resources/i18n/project_en_US.properties @@ -480,6 +480,7 @@ file.log.previous=behind file.log.upload=upload file.log.repository.add=Add repository file file.log.re-upload=re-upload +file.log.pull=Pull file file.name.cannot.be.empty=File name cannot be empty #file management over diff --git a/backend/framework/sdk/src/main/resources/i18n/project_zh_CN.properties b/backend/framework/sdk/src/main/resources/i18n/project_zh_CN.properties index cf347fb0d3..f4909cb286 100644 --- a/backend/framework/sdk/src/main/resources/i18n/project_zh_CN.properties +++ b/backend/framework/sdk/src/main/resources/i18n/project_zh_CN.properties @@ -479,6 +479,7 @@ file.log.previous=之前 file.log.upload=上传 file.log.repository.add=添加了存储库文件 file.log.re-upload=重新上传 +file.log.pull=拉取了文件 file.name.cannot.be.empty=文件名称不能为空 #file management over # template diff --git a/backend/framework/sdk/src/main/resources/i18n/project_zh_TW.properties b/backend/framework/sdk/src/main/resources/i18n/project_zh_TW.properties index 68f18bc141..fe766a5d7f 100644 --- a/backend/framework/sdk/src/main/resources/i18n/project_zh_TW.properties +++ b/backend/framework/sdk/src/main/resources/i18n/project_zh_TW.properties @@ -480,6 +480,7 @@ file.log.previous=之前 file.log.upload=上傳 file.log.repository.add=添加了存儲庫文件 file.log.re-upload=重新上傳 +file.log.pull=拉取了文件 file.name.cannot.be.empty=文件名稱不能為空 #file management over diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/controller/FileRepositoryController.java b/backend/services/project-management/src/main/java/io/metersphere/project/controller/FileRepositoryController.java index 26afb6f918..b79ad28aa1 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/controller/FileRepositoryController.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/controller/FileRepositoryController.java @@ -68,7 +68,14 @@ public class FileRepositoryController { @PostMapping("/add-file") @Operation(summary = "项目管理-文件管理-存储库-添加文件") @RequiresPermissions(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_ADD) - public String upload(@Validated @RequestBody RepositoryFileAddRequest request) throws Exception { + public String addFile(@Validated @RequestBody RepositoryFileAddRequest request) throws Exception { return fileRepositoryService.addFile(request, SessionUtils.getUserId()); } + + @GetMapping("/pull-file/{id}") + @Operation(summary = "项目管理-文件管理-存储库-更新文件") + @RequiresPermissions(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_ADD) + public String pullFile(@PathVariable String id) throws Exception { + return fileMetadataService.pullFile(id, SessionUtils.getUserId()); + } } diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/service/FileMetadataLogService.java b/backend/services/project-management/src/main/java/io/metersphere/project/service/FileMetadataLogService.java index 60f19293be..d23c066014 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/service/FileMetadataLogService.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/service/FileMetadataLogService.java @@ -65,7 +65,6 @@ public class FileMetadataLogService { .build().getLogDTO(); operationLogService.add(dto); } - public void saveUpdateLog(FileMetadata module, String projectId, String operator) { Project project = projectMapper.selectByPrimaryKey(projectId); LogDTO dto = LogDTOBuilder.builder() @@ -162,4 +161,21 @@ public class FileMetadataLogService { .build().getLogDTO(); operationLogService.add(dto); } + + public void saveFilePullLog(FileMetadata module, String operator) { + Project project = projectMapper.selectByPrimaryKey(module.getProjectId()); + LogDTO dto = LogDTOBuilder.builder() + .projectId(module.getProjectId()) + .organizationId(project.getOrganizationId()) + .type(OperationLogType.UPDATE.name()) + .module(logModule) + .method(HttpMethodConstants.GET.name()) + .path("/project/file/repository/pull-file") + .sourceId(module.getId()) + .content(Translator.get("file.log.pull") + " " + module.getName()) + .originalValue(JSON.toJSONBytes(module)) + .createUser(operator) + .build().getLogDTO(); + operationLogService.add(dto); + } } diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/service/FileMetadataService.java b/backend/services/project-management/src/main/java/io/metersphere/project/service/FileMetadataService.java index 54e97e2de8..e6a8625a15 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/service/FileMetadataService.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/service/FileMetadataService.java @@ -22,8 +22,10 @@ import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.LogUtils; import io.metersphere.sdk.util.TempFileUtils; import io.metersphere.sdk.util.Translator; +import io.metersphere.system.dto.sdk.RemoteFileAttachInfo; import io.metersphere.system.file.FileRequest; import io.metersphere.system.uid.IDGenerator; +import io.metersphere.system.utils.GitRepositoryUtil; import io.metersphere.system.utils.PageUtils; import io.metersphere.system.utils.Pager; import jakarta.annotation.Resource; @@ -333,33 +335,17 @@ public class FileMetadataService { if (oldFile == null) { throw new MSException(Translator.get("old.file.not.exist")); } - oldFile.setLatest(false); - fileMetadataMapper.updateByPrimaryKeySelective(oldFile); - //删除旧的预览文件 - TempFileUtils.deleteTmpFile(oldFile.getId()); - - long operationTime = System.currentTimeMillis(); - FileMetadata fileMetadata = new FileMetadata(); - fileMetadata.setId(IDGenerator.nextStr()); - fileMetadata.setStorage(oldFile.getStorage()); - fileMetadata.setProjectId(oldFile.getProjectId()); - fileMetadata.setModuleId(oldFile.getModuleId()); - fileMetadata.setName(oldFile.getName()); - fileMetadata.setType(oldFile.getType()); - fileMetadata.setCreateTime(operationTime); - fileMetadata.setCreateUser(oldFile.getCreateUser()); - fileMetadata.setUpdateTime(operationTime); - fileMetadata.setUpdateUser(operator); + if (!StringUtils.equals(oldFile.getStorage(), StorageType.MINIO.name())) { + //非minio类型文件不允许重新上传 + throw new MSException(Translator.get("file.not.exist")); + } + this.setFileVersionIsOld(oldFile, operator); + FileMetadata fileMetadata = this.genNewVersion(oldFile, operator); fileMetadata.setSize(uploadFile.getSize()); - fileMetadata.setRefId(oldFile.getRefId()); - fileMetadata.setEnable(oldFile.getEnable()); - fileMetadata.setLatest(true); fileMetadataMapper.insert(fileMetadata); - //记录日志 fileMetadataLogService.saveReUploadLog(fileMetadata, operator); - // 上传文件 String filePath = this.uploadFile(fileMetadata, uploadFile); FileMetadata updateFileMetadata = new FileMetadata(); @@ -454,4 +440,69 @@ public class FileMetadataService { fileMetadataLogService.saveFileMoveLog(logList, request.getProjectId(), operator); } } + + private void setFileVersionIsOld(FileMetadata oldFile, String operator) { + //删除旧的预览文件 + TempFileUtils.deleteTmpFile(oldFile.getId()); + //更新文件版本分支 + FileMetadata updateModel = new FileMetadata(); + updateModel.setId(oldFile.getId()); + updateModel.setLatest(false); + updateModel.setUpdateTime(System.currentTimeMillis()); + updateModel.setUpdateUser(operator); + fileMetadataMapper.updateByPrimaryKeySelective(updateModel); + } + + private FileMetadata genNewVersion(FileMetadata oldFile, String operator) { + long operationTime = System.currentTimeMillis(); + FileMetadata fileMetadata = new FileMetadata(); + fileMetadata.setId(IDGenerator.nextStr()); + fileMetadata.setStorage(oldFile.getStorage()); + fileMetadata.setProjectId(oldFile.getProjectId()); + fileMetadata.setModuleId(oldFile.getModuleId()); + fileMetadata.setName(oldFile.getName()); + fileMetadata.setType(oldFile.getType()); + fileMetadata.setCreateTime(operationTime); + fileMetadata.setCreateUser(oldFile.getCreateUser()); + fileMetadata.setUpdateTime(operationTime); + fileMetadata.setPath(oldFile.getPath()); + fileMetadata.setUpdateUser(operator); + fileMetadata.setRefId(oldFile.getRefId()); + fileMetadata.setEnable(oldFile.getEnable()); + fileMetadata.setLatest(true); + return fileMetadata; + } + + public String pullFile(String fileId, String operator) { + FileMetadata oldFile = fileMetadataMapper.selectByPrimaryKey(fileId); + String returnFileId = fileId; + if (StringUtils.equals(oldFile.getStorage(), StorageType.GIT.name())) { + FileMetadataRepository metadataRepository = fileMetadataRepositoryMapper.selectByPrimaryKey(fileId); + FileModuleRepository moduleRepository = fileModuleRepositoryMapper.selectByPrimaryKey(oldFile.getModuleId()); + if (metadataRepository != null && moduleRepository != null) { + + GitRepositoryUtil repositoryUtils = new GitRepositoryUtil(moduleRepository.getUrl(), moduleRepository.getUserName(), moduleRepository.getToken()); + RemoteFileAttachInfo gitFileAttachInfo = repositoryUtils.selectLastCommitIdByBranch(metadataRepository.getBranch(), oldFile.getPath()); + + if (!StringUtils.equals(gitFileAttachInfo.getCommitId(), metadataRepository.getCommitId())) { + this.setFileVersionIsOld(oldFile, operator); + FileMetadata fileMetadata = this.genNewVersion(oldFile, operator); + fileMetadata.setSize(gitFileAttachInfo.getSize()); + fileMetadataMapper.insert(fileMetadata); + returnFileId = fileMetadata.getId(); + + FileMetadataRepository fileMetadataRepository = new FileMetadataRepository(); + fileMetadataRepository.setFileMetadataId(returnFileId); + fileMetadataRepository.setBranch(gitFileAttachInfo.getBranch()); + fileMetadataRepository.setCommitId(gitFileAttachInfo.getCommitId()); + fileMetadataRepository.setCommitMessage(gitFileAttachInfo.getCommitMessage()); + fileMetadataRepositoryMapper.insert(fileMetadataRepository); + + //记录日志 + fileMetadataLogService.saveFilePullLog(fileMetadata, operator); + } + } + } + return returnFileId; + } } diff --git a/backend/services/project-management/src/test/java/io/metersphere/project/controller/filemanagement/FileManagementControllerTests.java b/backend/services/project-management/src/test/java/io/metersphere/project/controller/filemanagement/FileManagementControllerTests.java index 749cca4277..6e79866280 100644 --- a/backend/services/project-management/src/test/java/io/metersphere/project/controller/filemanagement/FileManagementControllerTests.java +++ b/backend/services/project-management/src/test/java/io/metersphere/project/controller/filemanagement/FileManagementControllerTests.java @@ -556,7 +556,7 @@ public class FileManagementControllerTests extends BaseTest { FileUploadRequest fileUploadRequest = new FileUploadRequest(); fileUploadRequest.setProjectId(project.getId()); - //重新上传并修改文件版本 + //构建参数 FileReUploadRequest fileReUploadRequest = new FileReUploadRequest(); fileReUploadRequest.setFileId(reUploadFileId); String filePath = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/file_re-upload.JPG")).getPath(); @@ -565,6 +565,17 @@ public class FileManagementControllerTests extends BaseTest { paramMap.add("file", file); paramMap.add("request", JSON.toJSONString(fileReUploadRequest)); + //测试非minio文件不能重新上传 + FileMetadata updateModel = new FileMetadata(); + updateModel.setId(reUploadFileId); + updateModel.setStorage(StorageType.GIT.name()); + fileMetadataMapper.updateByPrimaryKeySelective(updateModel); + this.requestMultipart(FileManagementRequestUtils.URL_FILE_RE_UPLOAD, paramMap).andExpect(status().is5xxServerError()); + //测试完了改回去 + updateModel.setStorage(StorageType.MINIO.name()); + fileMetadataMapper.updateByPrimaryKeySelective(updateModel); + + //重新上传并修改文件版本 MvcResult mvcResult = this.requestMultipartWithOkAndReturn(FileManagementRequestUtils.URL_FILE_RE_UPLOAD, paramMap); String reUploadId = JSON.parseObject(mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class).getData().toString(); checkLog(reUploadId, OperationLogType.UPDATE, FileManagementRequestUtils.URL_FILE_RE_UPLOAD); diff --git a/backend/services/project-management/src/test/java/io/metersphere/project/controller/filemanagement/FileRepositoryControllerTest.java b/backend/services/project-management/src/test/java/io/metersphere/project/controller/filemanagement/FileRepositoryControllerTest.java index e06ae25db0..cfcb380c8e 100644 --- a/backend/services/project-management/src/test/java/io/metersphere/project/controller/filemanagement/FileRepositoryControllerTest.java +++ b/backend/services/project-management/src/test/java/io/metersphere/project/controller/filemanagement/FileRepositoryControllerTest.java @@ -362,7 +362,7 @@ public class FileRepositoryControllerTest extends BaseTest { request.setModuleId(repositoryId); MvcResult result = this.requestPostWithOkAndReturn(FileManagementRequestUtils.URL_FILE_REPOSITORY_FILE_ADD, request); String fileId = JSON.parseObject(result.getResponse().getContentAsString(), ResultHolder.class).getData().toString(); - this.checkFileRepositoryFile(fileId, request); + this.checkRepositoryFile(fileId, request); this.checkLog(fileId, OperationLogType.ADD, FileManagementRequestUtils.URL_FILE_REPOSITORY_FILE_ADD); fileList.add(fileId); //测试其他分支的多层目录的文件 @@ -374,7 +374,7 @@ public class FileRepositoryControllerTest extends BaseTest { request.setModuleId(repositoryId); result = this.requestPostWithOkAndReturn(FileManagementRequestUtils.URL_FILE_REPOSITORY_FILE_ADD, request); fileId = JSON.parseObject(result.getResponse().getContentAsString(), ResultHolder.class).getData().toString(); - this.checkFileRepositoryFile(fileId, request); + this.checkRepositoryFile(fileId, request); fileList.add(fileId); //测试隐藏文件 String folderFilePath2 = "test-folder/.keep"; @@ -384,7 +384,7 @@ public class FileRepositoryControllerTest extends BaseTest { request.setModuleId(repositoryId); result = this.requestPostWithOkAndReturn(FileManagementRequestUtils.URL_FILE_REPOSITORY_FILE_ADD, request); fileId = JSON.parseObject(result.getResponse().getContentAsString(), ResultHolder.class).getData().toString(); - this.checkFileRepositoryFile(fileId, request); + this.checkRepositoryFile(fileId, request); fileList.add(fileId); //测试添加jar包并且启用 request = new RepositoryFileAddRequest(); @@ -394,7 +394,7 @@ public class FileRepositoryControllerTest extends BaseTest { request.setModuleId(repositoryId); result = this.requestPostWithOkAndReturn(FileManagementRequestUtils.URL_FILE_REPOSITORY_FILE_ADD, request); fileId = JSON.parseObject(result.getResponse().getContentAsString(), ResultHolder.class).getData().toString(); - this.checkFileRepositoryFile(fileId, request); + this.checkRepositoryFile(fileId, request); fileList.add(fileId); //获取图片信息 request = new RepositoryFileAddRequest(); @@ -403,7 +403,7 @@ public class FileRepositoryControllerTest extends BaseTest { request.setModuleId(repositoryId); result = this.requestPostWithOkAndReturn(FileManagementRequestUtils.URL_FILE_REPOSITORY_FILE_ADD, request); fileId = JSON.parseObject(result.getResponse().getContentAsString(), ResultHolder.class).getData().toString(); - this.checkFileRepositoryFile(fileId, request); + this.checkRepositoryFile(fileId, request); this.picFileId = fileId; fileList.add(fileId); { @@ -481,8 +481,29 @@ public class FileRepositoryControllerTest extends BaseTest { Assertions.assertTrue(compressedResult.getResponse().getContentAsByteArray().length > 0); } + @Test + @Order(12) + public void repositoryPullFileTest() throws Exception { + if (StringUtils.isEmpty(picFileId)) { + this.repositoryAddFileTest(); + } + MvcResult mvcResult = this.requestGetWithOkAndReturn(String.format(FileManagementRequestUtils.URL_FILE_REPOSITORY_FILE_PULL, picFileId)); + String fileId = JSON.parseObject(mvcResult.getResponse().getContentAsString(), ResultHolder.class).getData().toString(); + //此时没有更新记录,应当相等 + Assertions.assertEquals(fileId, picFileId); - private void checkFileRepositoryFile(String fileId, RepositoryFileAddRequest request) { + //手动更改过去的commit id 达到pull更新的效果 + FileMetadataRepository updateRepository = new FileMetadataRepository(); + updateRepository.setFileMetadataId(picFileId); + updateRepository.setCommitId(IDGenerator.nextStr()); + fileMetadataRepositoryMapper.updateByPrimaryKeySelective(updateRepository); + mvcResult = this.requestGetWithOkAndReturn(String.format(FileManagementRequestUtils.URL_FILE_REPOSITORY_FILE_PULL, picFileId)); + fileId = JSON.parseObject(mvcResult.getResponse().getContentAsString(), ResultHolder.class).getData().toString(); + this.checkRepositoryFile(picFileId, fileId); + } + + + private void checkRepositoryFile(String fileId, RepositoryFileAddRequest request) { FileMetadataRepository repository = fileMetadataRepositoryMapper.selectByPrimaryKey(fileId); Assertions.assertEquals(repository.getBranch(), request.getBranch()); Assertions.assertNotNull(repository.getCommitId()); @@ -492,6 +513,24 @@ public class FileRepositoryControllerTest extends BaseTest { Assertions.assertEquals(fileMetadata.getStorage(), StorageType.GIT.name()); } + private void checkRepositoryFile(String fileId, String newFileId) { + FileMetadata oldMetadata = fileMetadataMapper.selectByPrimaryKey(fileId); + FileMetadataRepository oldRepository = fileMetadataRepositoryMapper.selectByPrimaryKey(fileId); + FileMetadata newMetadata = fileMetadataMapper.selectByPrimaryKey(newFileId); + FileMetadataRepository newRepository = fileMetadataRepositoryMapper.selectByPrimaryKey(newFileId); + + Assertions.assertNotEquals(fileId, newFileId); + Assertions.assertEquals(oldMetadata.getName(), newMetadata.getName()); + Assertions.assertEquals(oldMetadata.getPath(), newMetadata.getPath()); + Assertions.assertEquals(oldMetadata.getStorage(), newMetadata.getStorage()); + Assertions.assertEquals(oldMetadata.getModuleId(), newMetadata.getModuleId()); + Assertions.assertEquals(oldMetadata.getType(), newMetadata.getType()); + Assertions.assertEquals(oldMetadata.getRefId(), newMetadata.getRefId()); + + Assertions.assertEquals(oldRepository.getBranch(), newRepository.getBranch()); + Assertions.assertNotEquals(oldRepository.getFileMetadataId(), newRepository.getFileMetadataId()); + } + protected MvcResult downloadFile(String url, Object... uriVariables) throws Exception { return mockMvc.perform(getRequestBuilder(url, uriVariables)) .andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM_VALUE)) diff --git a/backend/services/project-management/src/test/java/io/metersphere/project/utils/FileManagementRequestUtils.java b/backend/services/project-management/src/test/java/io/metersphere/project/utils/FileManagementRequestUtils.java index 624ae599a8..3b6edaea9a 100644 --- a/backend/services/project-management/src/test/java/io/metersphere/project/utils/FileManagementRequestUtils.java +++ b/backend/services/project-management/src/test/java/io/metersphere/project/utils/FileManagementRequestUtils.java @@ -54,8 +54,7 @@ public class FileManagementRequestUtils { public static final String URL_FILE_REPOSITORY_CREATE = "/project/file/repository/add-repository"; //修改存储库 public static final String URL_FILE_REPOSITORY_UPDATE = "/project/file/repository/update-repository"; - //添加文件 public static final String URL_FILE_REPOSITORY_FILE_ADD = "/project/file/repository/add-file"; - + public static final String URL_FILE_REPOSITORY_FILE_PULL = "/project/file/repository/pull-file/%s"; } diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/utils/GitRepositoryUtil.java b/backend/services/system-setting/src/main/java/io/metersphere/system/utils/GitRepositoryUtil.java index c54b71d57a..443c48d941 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/utils/GitRepositoryUtil.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/utils/GitRepositoryUtil.java @@ -83,7 +83,6 @@ public class GitRepositoryUtil { } public RemoteFileAttachInfo selectLastCommitIdByBranch(String branch, String filePath) { - RemoteFileAttachInfo attachInfo; InMemoryRepository repo = null; TreeWalk treeWalk = null; try { @@ -105,8 +104,7 @@ public class GitRepositoryUtil { if (StringUtils.isEmpty(fileLastCommitId)) { fileLastCommitId = lastCommitId.getName(); } - attachInfo = new RemoteFileAttachInfo(repositoryUrl, userName, token, branch, fileLastCommitId, filePath, commit.getFullMessage(), loader.getSize()); - return attachInfo; + return new RemoteFileAttachInfo(repositoryUrl, userName, token, branch, fileLastCommitId, filePath, commit.getFullMessage(), loader.getSize()); } } } catch (Exception e) { @@ -117,7 +115,7 @@ public class GitRepositoryUtil { } this.closeConnection(repo); } - return null; + return new RemoteFileAttachInfo(); } private String getFileLastCommitId(ObjectId objectId, String filePath) throws Exception {