From 821025b50d89f84f4e645f117622a168d6126304 Mon Sep 17 00:00:00 2001 From: Kaiser-Yang <624626089@qq.com> Date: Wed, 25 Sep 2024 19:05:40 +0800 Subject: [PATCH 1/2] Finish delete and update repo, and delete user We finish the APIs for deleting and updating repository. For the deleting user API, we now will remove all the ssh-keys uploaded by the user, but not the repositories. Besides, we find that we should not trust the data from the client side, such as the `userId` from request body, we must get this data from the database to check if the operation is allowed. See #20 and #32. --- script/deploy_helper.py | 4 +- .../cmipt/gcs/constant/ApiPathConstant.java | 4 + .../gcs/controller/RepositoryController.java | 107 +++++++++++++++++- .../gcs/controller/SshKeyController.java | 30 +++-- .../cmipt/gcs/enumeration/ErrorCodeEnum.java | 8 +- .../gcs/exception/GlobalExceptionHandler.java | 5 + .../java/edu/cmipt/gcs/filter/JwtFilter.java | 29 ++--- .../gcs/pojo/repository/RepositoryPO.java | 4 + .../gcs/pojo/repository/RepositoryVO.java | 2 + .../edu/cmipt/gcs/pojo/ssh/SshKeyDTO.java | 5 - .../java/edu/cmipt/gcs/pojo/ssh/SshKeyPO.java | 8 +- .../gcs/service/RepositoryServiceImpl.java | 49 ++++++-- .../cmipt/gcs/service/UserServiceImpl.java | 28 ++++- src/main/resources/message/message.properties | 6 +- .../edu/cmipt/gcs/constant/TestConstant.java | 2 + .../controller/RepositoryControllerTest.java | 86 ++++++++++++-- .../gcs/controller/SshKeyControllerTest.java | 9 +- .../gcs/controller/UserControllerTest.java | 5 +- 18 files changed, 331 insertions(+), 60 deletions(-) diff --git a/script/deploy_helper.py b/script/deploy_helper.py index 391e870..8250640 100644 --- a/script/deploy_helper.py +++ b/script/deploy_helper.py @@ -377,8 +377,8 @@ def deploy_on_ubuntu(config): command_checker(res, message) create_or_update_user(config.gitUserName, config.gitUserPassword) create_or_update_user(config.serviceUser, config.serviceUserPassword) - # let the service user can use git and tee commands as the git user without password - sudoers_entry = f"{config.serviceUser} ALL=(git) NOPASSWD: /usr/bin/git, /usr/bin/tee" + # let the service user can use git, rm and tee commands as the git user without password + sudoers_entry = f"{config.serviceUser} ALL=(git) NOPASSWD: /usr/bin/git, /usr/bin/tee, /usr/bin/rm" res = subprocess.run(f"echo '{sudoers_entry}' | {sudo_cmd} tee /etc/sudoers.d/{config.serviceUser}", shell=True); command_checker(res.returncode, f"Failed to create /etc/sudoers.d/{config.serviceUser}") res = subprocess.run(f"{sudo_cmd} chmod 440 /etc/sudoers.d/{config.serviceUser}", shell=True) diff --git a/src/main/java/edu/cmipt/gcs/constant/ApiPathConstant.java b/src/main/java/edu/cmipt/gcs/constant/ApiPathConstant.java index 80e2b16..9c15c64 100644 --- a/src/main/java/edu/cmipt/gcs/constant/ApiPathConstant.java +++ b/src/main/java/edu/cmipt/gcs/constant/ApiPathConstant.java @@ -32,6 +32,10 @@ public class ApiPathConstant { public static final String REPOSITORY_API_PREFIX = ALL_API_PREFIX + "/repository"; public static final String REPOSITORY_CREATE_REPOSITORY_API_PATH = REPOSITORY_API_PREFIX + "/create"; + public static final String REPOSITORY_DELETE_REPOSITORY_API_PATH = + REPOSITORY_API_PREFIX + "/delete"; + public static final String REPOSITORY_UPDATE_REPOSITORY_API_PATH = + REPOSITORY_API_PREFIX + "/update"; public static final String SSH_KEY_API_PREFIX = ALL_API_PREFIX + "/ssh"; public static final String SSH_KEY_UPLOAD_SSH_KEY_API_PATH = SSH_KEY_API_PREFIX + "/upload"; diff --git a/src/main/java/edu/cmipt/gcs/controller/RepositoryController.java b/src/main/java/edu/cmipt/gcs/controller/RepositoryController.java index 546e1b5..b1d94f4 100644 --- a/src/main/java/edu/cmipt/gcs/controller/RepositoryController.java +++ b/src/main/java/edu/cmipt/gcs/controller/RepositoryController.java @@ -8,28 +8,36 @@ import edu.cmipt.gcs.exception.GenericException; import edu.cmipt.gcs.pojo.repository.RepositoryDTO; import edu.cmipt.gcs.pojo.repository.RepositoryPO; +import edu.cmipt.gcs.pojo.repository.RepositoryVO; import edu.cmipt.gcs.service.RepositoryService; import edu.cmipt.gcs.util.JwtUtil; import edu.cmipt.gcs.validation.group.CreateGroup; - +import edu.cmipt.gcs.validation.group.UpdateGroup; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @Tag(name = "Repository", description = "Repository Related APIs") public class RepositoryController { + private static final Logger logger = LoggerFactory.getLogger(SshKeyController.class); @Autowired private RepositoryService repositoryService; @PostMapping(ApiPathConstant.REPOSITORY_CREATE_REPOSITORY_API_PATH) @@ -49,6 +57,9 @@ public class RepositoryController { public void createRepository( @Validated(CreateGroup.class) @RequestBody RepositoryDTO repository, @RequestHeader(HeaderParameter.ACCESS_TOKEN) String accessToken) { + if (repository.isPrivate() != null && repository.isPrivate()) { + throw new GenericException(ErrorCodeEnum.OPERATION_NOT_IMPLEMENTED, "private repository is not implemented"); + } String userId = JwtUtil.getId(accessToken); RepositoryPO repositoryPO = new RepositoryPO(repository, userId); QueryWrapper queryWrapper = new QueryWrapper<>(); @@ -61,4 +72,98 @@ public void createRepository( throw new GenericException(ErrorCodeEnum.REPOSITORY_CREATE_FAILED, repository); } } + + @DeleteMapping(ApiPathConstant.REPOSITORY_DELETE_REPOSITORY_API_PATH) + @Operation( + summary = "Delete a repository", + description = "Delete a repository with the given id", + tags = {"Repository", "Delete Method"} + ) + @Parameters({ + @Parameter( + name = HeaderParameter.ACCESS_TOKEN, + description = "Access token", + required = true, + in = ParameterIn.HEADER, + schema = @Schema(implementation = String.class) + ), + @Parameter( + name = "id", + description = "Repository id", + required = true, + in = ParameterIn.QUERY, + schema = @Schema(implementation = Long.class) + ) + }) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "Repository deleted successfully"), + @ApiResponse(responseCode = "403", description = "Access denied"), + @ApiResponse(responseCode = "404", description = "Repository not found") + }) + public void deleteRepository( + @RequestHeader(HeaderParameter.ACCESS_TOKEN) String accessToken, + @RequestParam("id") Long id + ) { + var repository = repositoryService.getById(id); + if (repository == null) { + throw new GenericException(ErrorCodeEnum.REPOSITORY_NOT_FOUND, id); + } + String userId = JwtUtil.getId(accessToken); + if (!userId.equals(repository.getUserId().toString())) { + logger.info("User[{}] tried to delete repository of user[{}]", userId, repository.getUserId()); + throw new GenericException(ErrorCodeEnum.ACCESS_DENIED); + } + if (!repositoryService.removeById(id)) { + throw new GenericException(ErrorCodeEnum.REPOSITORY_DELETE_FAILED, id); + } + } + + @PostMapping(ApiPathConstant.REPOSITORY_UPDATE_REPOSITORY_API_PATH) + @Operation( + summary = "Update a repository", + description = "Update a repository with the given information", + tags = {"Repository", "Post Method"} + ) + @Parameter( + name = HeaderParameter.ACCESS_TOKEN, + description = "Access token", + required = true, + in = ParameterIn.HEADER, + schema = @Schema(implementation = String.class) + ) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "Repository updated successfully"), + @ApiResponse(responseCode = "403", description = "Access denied"), + @ApiResponse(responseCode = "404", description = "Repository not found"), + @ApiResponse(responseCode = "501", description = "Update repository name is not implemented") + }) + public ResponseEntity updateRepository( + @Validated(UpdateGroup.class) @RequestBody RepositoryDTO repository, + @RequestHeader(HeaderParameter.ACCESS_TOKEN) String accessToken + ) { + Long id = null; + try { + id = Long.valueOf(repository.id()); + } catch (NumberFormatException e) { + logger.error(e.getMessage()); + throw new GenericException(ErrorCodeEnum.MESSAGE_CONVERSION_ERROR); + } + var repositoryPO = repositoryService.getById(id); + if (repositoryPO == null) { + throw new GenericException(ErrorCodeEnum.REPOSITORY_NOT_FOUND, id); + } + String userId = JwtUtil.getId(accessToken); + if (!userId.equals(repositoryPO.getUserId().toString())) { + logger.info("User[{}] tried to update repository of user[{}]", userId, repositoryPO.getUserId()); + throw new GenericException(ErrorCodeEnum.ACCESS_DENIED); + } + if (repository.repositoryName() != null && + !repository.repositoryName().equals(repositoryService.getById(id).getRepositoryName())) { + throw new GenericException(ErrorCodeEnum.OPERATION_NOT_IMPLEMENTED, "update repository name is not implemented"); + } + if (!repositoryService.updateById(new RepositoryPO(repository))) { + throw new GenericException(ErrorCodeEnum.REPOSITORY_UPDATE_FAILED, repository); + } + return ResponseEntity.ok().body(new RepositoryVO(repositoryService.getById(id))); + } } diff --git a/src/main/java/edu/cmipt/gcs/controller/SshKeyController.java b/src/main/java/edu/cmipt/gcs/controller/SshKeyController.java index 2f0d0fd..020046f 100644 --- a/src/main/java/edu/cmipt/gcs/controller/SshKeyController.java +++ b/src/main/java/edu/cmipt/gcs/controller/SshKeyController.java @@ -73,7 +73,7 @@ public class SshKeyController { public void uploadSshKey( @Validated(CreateGroup.class) @RequestBody SshKeyDTO sshKeyDTO, @RequestHeader(HeaderParameter.ACCESS_TOKEN) String accessToken) { - if (!sshKeyService.save(new SshKeyPO(sshKeyDTO))) { + if (!sshKeyService.save(new SshKeyPO(sshKeyDTO, JwtUtil.getId(accessToken)))) { throw new GenericException(ErrorCodeEnum.SSH_KEY_UPLOAD_FAILED, sshKeyDTO); } } @@ -101,14 +101,13 @@ public void uploadSshKey( public void deleteSshKey( @RequestHeader(HeaderParameter.ACCESS_TOKEN) String accessToken, @RequestParam("id") Long id) { - var res = sshKeyService.getById(id); - if (res == null) { + var sshKeyPO = sshKeyService.getById(id); + if (sshKeyPO == null) { throw new GenericException(ErrorCodeEnum.SSH_KEY_NOT_FOUND, id); } String idInToken = JwtUtil.getId(accessToken); - String idInRes = res.getUserId().toString(); - if (!idInRes.equals(idInToken)) { - logger.info("User[{}] tried to get SSH key of user[{}]", idInToken, idInRes); + if (!idInToken.equals(sshKeyPO.getUserId().toString())) { + logger.info("User[{}] tried to delete SSH key of user[{}]", idInToken, sshKeyPO.getUserId()); throw new GenericException(ErrorCodeEnum.ACCESS_DENIED); } if (!sshKeyService.removeById(id)) { @@ -137,7 +136,24 @@ public void deleteSshKey( content = @Content(schema = @Schema(implementation = ErrorVO.class))) }) public ResponseEntity updateSshKey( - @Validated(UpdateGroup.class) @RequestBody SshKeyDTO sshKeyDTO) { + @Validated(UpdateGroup.class) @RequestBody SshKeyDTO sshKeyDTO, + @RequestHeader(HeaderParameter.ACCESS_TOKEN) String accessToken) { + Long id = null; + try { + id = Long.valueOf(sshKeyDTO.id()); + } catch (NumberFormatException e) { + logger.error(e.getMessage()); + throw new GenericException(ErrorCodeEnum.MESSAGE_CONVERSION_ERROR); + } + var sshKeyPO = sshKeyService.getById(id); + if (sshKeyPO == null) { + throw new GenericException(ErrorCodeEnum.SSH_KEY_NOT_FOUND, id); + } + String idInToken = JwtUtil.getId(accessToken); + if (!idInToken.equals(sshKeyPO.getUserId().toString())) { + logger.info("User[{}] tried to update SSH key of user[{}]", idInToken, sshKeyPO.getUserId()); + throw new GenericException(ErrorCodeEnum.ACCESS_DENIED); + } if (!sshKeyService.updateById(new SshKeyPO(sshKeyDTO))) { throw new GenericException(ErrorCodeEnum.SSH_KEY_UPDATE_FAILED, sshKeyDTO); } diff --git a/src/main/java/edu/cmipt/gcs/enumeration/ErrorCodeEnum.java b/src/main/java/edu/cmipt/gcs/enumeration/ErrorCodeEnum.java index 51568bf..c47bb1d 100644 --- a/src/main/java/edu/cmipt/gcs/enumeration/ErrorCodeEnum.java +++ b/src/main/java/edu/cmipt/gcs/enumeration/ErrorCodeEnum.java @@ -48,12 +48,14 @@ public enum ErrorCodeEnum { REPOSITORYDTO_WATCHER_MIN("RepositoryDTO.watcher.Min"), REPOSITORYNAME_PATTERN_MISMATCH("REPOSITORYNAME_PATTERN_MISMATCH"), + REPOSITORY_NOT_FOUND("REPOSITORY_NOT_FOUND"), REPOSITORY_ALREADY_EXISTS("REPOSITORY_ALREADY_EXISTS"), REPOSITORY_CREATE_FAILED("REPOSITORY_CREATE_FAILED"), + REPOSITORY_UPDATE_FAILED("REPOSITORY_UPDATE_FAILED"), + REPOSITORY_DELETE_FAILED("REPOSITORY_DELETE_FAILED"), SSHKEYDTO_ID_NULL("SshKeyDTO.id.Null"), SSHKEYDTO_ID_NOTNULL("SshKeyDTO.id.NotNull"), - SSHKEYDTO_USERID_NOTBLANK("SshKeyDTO.userId.NotBlank"), SSHKEYDTO_NAME_NOTBLANK("SshKeyDTO.name.NotBlank"), SSHKEYDTO_NAME_SIZE("SshKeyDTO.name.Size"), SSHKEYDTO_PUBLICKEY_NOTBLANK("SshKeyDTO.publicKey.NotBlank"), @@ -62,7 +64,9 @@ public enum ErrorCodeEnum { SSH_KEY_UPLOAD_FAILED("SSH_KEY_UPLOAD_FAILED"), SSH_KEY_UPDATE_FAILED("SSH_KEY_UPDATE_FAILED"), SSH_KEY_DELETE_FAILED("SSH_KEY_DELETE_FAILED"), - SSH_KEY_NOT_FOUND("SSH_KEY_NOT_FOUND"); + SSH_KEY_NOT_FOUND("SSH_KEY_NOT_FOUND"), + + OPERATION_NOT_IMPLEMENTED("OPERATION_NOT_IMPLEMENTED"); // code means the error code in the message.properties private String code; diff --git a/src/main/java/edu/cmipt/gcs/exception/GlobalExceptionHandler.java b/src/main/java/edu/cmipt/gcs/exception/GlobalExceptionHandler.java index 3618238..36561a2 100644 --- a/src/main/java/edu/cmipt/gcs/exception/GlobalExceptionHandler.java +++ b/src/main/java/edu/cmipt/gcs/exception/GlobalExceptionHandler.java @@ -74,8 +74,13 @@ public ResponseEntity handleGenericException( return ResponseEntity.status(HttpStatus.FORBIDDEN) .body(new ErrorVO(e.getCode(), e.getMessage())); case USER_NOT_FOUND: + case SSH_KEY_NOT_FOUND: + case REPOSITORY_NOT_FOUND: return ResponseEntity.status(HttpStatus.NOT_FOUND) .body(new ErrorVO(e.getCode(), e.getMessage())); + case OPERATION_NOT_IMPLEMENTED: + return ResponseEntity.status(HttpStatus.NOT_IMPLEMENTED) + .body(new ErrorVO(e.getCode(), e.getMessage())); default: return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(new ErrorVO(e.getCode(), e.getMessage())); diff --git a/src/main/java/edu/cmipt/gcs/filter/JwtFilter.java b/src/main/java/edu/cmipt/gcs/filter/JwtFilter.java index f93d591..e520966 100644 --- a/src/main/java/edu/cmipt/gcs/filter/JwtFilter.java +++ b/src/main/java/edu/cmipt/gcs/filter/JwtFilter.java @@ -198,26 +198,15 @@ private void authorize(HttpServletRequest request, String accessToken, String re // pass } else if (request.getRequestURI() .equals(ApiPathConstant.SSH_KEY_UPLOAD_SSH_KEY_API_PATH)) { - String idInToken = JwtUtil.getId(accessToken); - String idInBody = getFromRequestBody(request, "userId"); - if (!idInToken.equals(idInBody)) { - logger.info( - "User[{}] tried to upload SSH key of user[{}]", - idInToken, - idInBody); - throw new GenericException(ErrorCodeEnum.ACCESS_DENIED); - } + // pass } else if (request.getRequestURI() .equals(ApiPathConstant.SSH_KEY_UPDATE_SSH_KEY_API_PATH)) { - String idInToken = JwtUtil.getId(accessToken); - String idInBody = getFromRequestBody(request, "userId"); - if (!idInToken.equals(idInBody)) { - logger.info( - "User[{}] tried to update SSH key of user[{}]", - idInToken, - idInBody); - throw new GenericException(ErrorCodeEnum.ACCESS_DENIED); - } + // this will be checked in controller + // because we must query the database to get the user id of the ssh key + } else if (request.getRequestURI() + .equals(ApiPathConstant.REPOSITORY_UPDATE_REPOSITORY_API_PATH)) { + // this will be checked in controller + // because we must query the database to get the user id of the repository } else { throw new GenericException(ErrorCodeEnum.ACCESS_DENIED); } @@ -242,6 +231,10 @@ private void authorize(HttpServletRequest request, String accessToken, String re .equals(ApiPathConstant.SSH_KEY_DELETE_SSH_KEY_API_PATH)) { // this will be checked in controller // because we must query the database to get the user id of the ssh key + } else if (request.getRequestURI() + .equals(ApiPathConstant.REPOSITORY_DELETE_REPOSITORY_API_PATH)) { + // this will be checked in controller + // because we must query the database to get the user id of the repository } else { throw new GenericException(ErrorCodeEnum.ACCESS_DENIED); } diff --git a/src/main/java/edu/cmipt/gcs/pojo/repository/RepositoryPO.java b/src/main/java/edu/cmipt/gcs/pojo/repository/RepositoryPO.java index 4d6c0b2..3768c07 100644 --- a/src/main/java/edu/cmipt/gcs/pojo/repository/RepositoryPO.java +++ b/src/main/java/edu/cmipt/gcs/pojo/repository/RepositoryPO.java @@ -45,4 +45,8 @@ public RepositoryPO(RepositoryDTO repositoryDTO, String userId) { this.fork = repositoryDTO.fork(); this.watcher = repositoryDTO.watcher(); } + + public RepositoryPO(RepositoryDTO repositoryDTO) { + this(repositoryDTO, null); + } } diff --git a/src/main/java/edu/cmipt/gcs/pojo/repository/RepositoryVO.java b/src/main/java/edu/cmipt/gcs/pojo/repository/RepositoryVO.java index 6152a34..5964c97 100644 --- a/src/main/java/edu/cmipt/gcs/pojo/repository/RepositoryVO.java +++ b/src/main/java/edu/cmipt/gcs/pojo/repository/RepositoryVO.java @@ -7,6 +7,7 @@ public record RepositoryVO( @Schema(description = "Repository Name") String repositoryName, @Schema(description = "Repository Description") String repositoryDescription, @Schema(description = "Whether or Not Private Repo") Boolean isPrivate, + @Schema(description = "Owner ID") Long userId, @Schema(description = "Star Count") Integer star, @Schema(description = "Fork Count") Integer fork, @Schema(description = "Watcher Count") Integer watcher) { @@ -16,6 +17,7 @@ public RepositoryVO(RepositoryPO repositoryPO) { repositoryPO.getRepositoryName(), repositoryPO.getRepositoryDescription(), repositoryPO.getIsPrivate(), + repositoryPO.getUserId(), repositoryPO.getStar(), repositoryPO.getFork(), repositoryPO.getWatcher()); diff --git a/src/main/java/edu/cmipt/gcs/pojo/ssh/SshKeyDTO.java b/src/main/java/edu/cmipt/gcs/pojo/ssh/SshKeyDTO.java index 07b4c5a..6bae458 100644 --- a/src/main/java/edu/cmipt/gcs/pojo/ssh/SshKeyDTO.java +++ b/src/main/java/edu/cmipt/gcs/pojo/ssh/SshKeyDTO.java @@ -19,11 +19,6 @@ public record SshKeyDTO( groups = UpdateGroup.class, message = "SSHKEYDTO_ID_NOTNULL {SshKeyDTO.id.NotNull}") String id, - @Schema(description = "User ID") - @NotBlank( - groups = {CreateGroup.class, UpdateGroup.class}, - message = "SSHKEYDTO_USERID_NOTBLANK {SshKeyDTO.userId.NotBlank}") - String userId, @Schema(description = "Name", example = "My SSH Key") @NotBlank( groups = {CreateGroup.class, UpdateGroup.class}, diff --git a/src/main/java/edu/cmipt/gcs/pojo/ssh/SshKeyPO.java b/src/main/java/edu/cmipt/gcs/pojo/ssh/SshKeyPO.java index fb30763..e1c78be 100644 --- a/src/main/java/edu/cmipt/gcs/pojo/ssh/SshKeyPO.java +++ b/src/main/java/edu/cmipt/gcs/pojo/ssh/SshKeyPO.java @@ -20,18 +20,22 @@ public class SshKeyPO { private LocalDateTime gmtUpdated; @TableLogic private LocalDateTime gmtDeleted; - public SshKeyPO(SshKeyDTO sshKeyDTO) { + public SshKeyPO(SshKeyDTO sshKeyDTO, String userId) { try { this.id = Long.valueOf(sshKeyDTO.id()); } catch (NumberFormatException e) { this.id = null; } try { - this.userId = Long.valueOf(sshKeyDTO.userId()); + this.userId = Long.valueOf(userId); } catch (NumberFormatException e) { this.userId = null; } this.name = sshKeyDTO.name(); this.publicKey = sshKeyDTO.publicKey(); } + + public SshKeyPO(SshKeyDTO sshKeyDTO) { + this(sshKeyDTO, null); + } } diff --git a/src/main/java/edu/cmipt/gcs/service/RepositoryServiceImpl.java b/src/main/java/edu/cmipt/gcs/service/RepositoryServiceImpl.java index 9b73042..7ec3902 100644 --- a/src/main/java/edu/cmipt/gcs/service/RepositoryServiceImpl.java +++ b/src/main/java/edu/cmipt/gcs/service/RepositoryServiceImpl.java @@ -15,6 +15,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.io.Serializable; import java.nio.file.Files; import java.nio.file.Paths; @@ -38,13 +39,7 @@ public boolean save(RepositoryPO repositoryPO) { logger.error("Failed to save repository to database"); return false; } - String repositorySavePath = - Paths.get( - GitConstant.GIT_REPOSITORY_DIRECTORY, - userMapper.selectById(repositoryPO.getUserId()).getUsername(), - repositoryPO.getRepositoryName() - + GitConstant.GIT_REPOSITORY_SUFFIX) - .toString(); + String repositorySavePath = getRepositorySavePath(repositoryPO); // check if the repositorySavePath has been created, if so, remove it // this may occur, if the last creation failed if (Files.exists(Paths.get(repositorySavePath))) { @@ -92,4 +87,44 @@ public boolean save(RepositoryPO repositoryPO) { } return true; } + + @Override + @Transactional + public boolean removeById(Serializable id) { + RepositoryPO repositoryPO = super.getById(id); + assert repositoryPO != null; + if (!super.removeById(id)) { + logger.error("Failed to remove repository from database"); + return false; + } + String repositorySavePath = getRepositorySavePath(repositoryPO); + try { + ProcessBuilder dirRemover = + new ProcessBuilder( + "sudo", + "-u", + GitConstant.GIT_USER_NAME, + "rm", + "-rf", + repositorySavePath); + Process process = dirRemover.start(); + if (process.waitFor() != 0) { + throw new GenericException( + ErrorCodeEnum.REPOSITORY_DELETE_FAILED, + process.errorReader().lines().toList().toString()); + } + } catch (Exception e) { + logger.error("Failed to remove repository directory: {}", e.getMessage()); + throw new GenericException(ErrorCodeEnum.REPOSITORY_DELETE_FAILED, e.getMessage()); + } + return true; + } + + private String getRepositorySavePath(RepositoryPO repositoryPO) { + return Paths.get( + GitConstant.GIT_REPOSITORY_DIRECTORY, + repositoryPO.getUserId().toString(), + repositoryPO.getRepositoryName() + GitConstant.GIT_REPOSITORY_SUFFIX) + .toString(); + } } diff --git a/src/main/java/edu/cmipt/gcs/service/UserServiceImpl.java b/src/main/java/edu/cmipt/gcs/service/UserServiceImpl.java index 275d752..95a30f3 100644 --- a/src/main/java/edu/cmipt/gcs/service/UserServiceImpl.java +++ b/src/main/java/edu/cmipt/gcs/service/UserServiceImpl.java @@ -1,11 +1,37 @@ package edu.cmipt.gcs.service; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import edu.cmipt.gcs.dao.UserMapper; import edu.cmipt.gcs.pojo.user.UserPO; +import edu.cmipt.gcs.pojo.ssh.SshKeyPO; +import java.io.Serializable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service -public class UserServiceImpl extends ServiceImpl implements UserService {} +public class UserServiceImpl extends ServiceImpl implements UserService { + private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class); + + @Autowired + SshKeyService sshKeyService; + + @Override + @Transactional + public boolean removeById(Serializable id) { + if (!super.removeById(id)) { + logger.error("Failed to remove user from database"); + return false; + } + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("user_id", id); + sshKeyService.list(wrapper).forEach(sshKey -> sshKeyService.removeById(sshKey.getId())); + return true; + } +} diff --git a/src/main/resources/message/message.properties b/src/main/resources/message/message.properties index fca220f..4f9d6ce 100644 --- a/src/main/resources/message/message.properties +++ b/src/main/resources/message/message.properties @@ -44,12 +44,14 @@ RepositoryDTO.watcher.Null=Watcher must be null when creating a new repository RepositoryDTO.watcher.Min=Watcher must be greater than or equal to {value} REPOSITORYNAME_PATTERN_MISMATCH=Repository name can only be alphanumeric, hyphen or underline +REPOSITORY_NOT_FOUND=Repository not found: {} REPOSITORY_ALREADY_EXISTS=Repository already exists: {} REPOSITORY_CREATE_FAILED=Repository create failed: {} +REPOSITORY_UPDATE_FAILED=Repository update failed: {} +REPOSITORY_DELETE_FAILED=Repository delete failed: {} SshKeyDTO.id.Null=SSH key id must be null when creating a new SSH key SshKeyDTO.id.NotNull=SSH key id cannot be null -SshKeyDTO.userId.NotBlank=SSH key user id cannot be blank SshKeyDTO.name.NotBlank=SSH key name cannot be blank SshKeyDTO.name.Size=SSH key name must be between {min} and {max} characters SshKeyDTO.publicKey.NotBlank=SSH key public key cannot be blank @@ -59,3 +61,5 @@ SSH_KEY_UPLOAD_FAILED=SSH key upload failed: {} SSH_KEY_UPDATE_FAILED=SSH key update failed: {} SSH_KEY_DELETE_FAILED=SSH key delete failed: {} SSH_KEY_NOT_FOUND=SSH key not found: {} + +OPERATION_NOT_IMPLEMENTED=Operation not implemented: {} diff --git a/src/test/java/edu/cmipt/gcs/constant/TestConstant.java b/src/test/java/edu/cmipt/gcs/constant/TestConstant.java index 860456e..260d9a2 100644 --- a/src/test/java/edu/cmipt/gcs/constant/TestConstant.java +++ b/src/test/java/edu/cmipt/gcs/constant/TestConstant.java @@ -15,6 +15,8 @@ public class TestConstant { public static String OTHER_EMAIL = OTHER_USERNAME + "@cmipt.edu"; public static String OTHER_ACCESS_TOKEN; public static String OTHER_REFRESH_TOKEN; + public static String REPOSITORY_ID; + public static String REPOSITORY_NAME; public static Integer REPOSITORY_SIZE = 10; public static Integer SSH_KEY_SIZE = 10; public static String SSH_KEY_ID; diff --git a/src/test/java/edu/cmipt/gcs/controller/RepositoryControllerTest.java b/src/test/java/edu/cmipt/gcs/controller/RepositoryControllerTest.java index 7dd19d7..359d88d 100644 --- a/src/test/java/edu/cmipt/gcs/controller/RepositoryControllerTest.java +++ b/src/test/java/edu/cmipt/gcs/controller/RepositoryControllerTest.java @@ -1,16 +1,27 @@ package edu.cmipt.gcs.controller; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import edu.cmipt.gcs.constant.ApiPathConstant; import edu.cmipt.gcs.constant.HeaderParameter; import edu.cmipt.gcs.constant.TestConstant; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.json.JsonParserFactory; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.core.Ordered; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; @@ -21,19 +32,15 @@ */ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @AutoConfigureMockMvc +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class RepositoryControllerTest { @Autowired private MockMvc mvc; @Test + @Order(Ordered.HIGHEST_PRECEDENCE) public void testCreateRepositoryValid() throws Exception { - String isPrivate = ""; String repositoryName = ""; for (int i = 0; i < TestConstant.REPOSITORY_SIZE; i++) { - if (i % 2 == 0) { - isPrivate = "true"; - } else { - isPrivate = "false"; - } repositoryName = String.valueOf(i); mvc.perform( post(ApiPathConstant.REPOSITORY_CREATE_REPOSITORY_API_PATH) @@ -42,12 +49,73 @@ public void testCreateRepositoryValid() throws Exception { .content( """ { - "repositoryName": "%s", - "isPrivate": %s + "repositoryName": "%s" } """ - .formatted(repositoryName, isPrivate))) + .formatted(repositoryName))) .andExpect(status().isOk()); } + var content = + mvc.perform( + get(ApiPathConstant.USER_PAGE_USER_REPOSITORY_API_PATH) + .header(HeaderParameter.ACCESS_TOKEN, TestConstant.ACCESS_TOKEN) + .param("id", TestConstant.ID) + .param("page", "1") + .param("size", TestConstant.REPOSITORY_SIZE.toString())) + .andExpectAll( + status().isOk(), + jsonPath("$").isArray(), + jsonPath("$.length()").value(TestConstant.REPOSITORY_SIZE)).andReturn().getResponse().getContentAsString(); + content = JsonParserFactory.getJsonParser().parseList(content).get(0).toString(); + Matcher matcher = Pattern.compile("id=(\\d+),").matcher(content); + matcher.find(); + TestConstant.REPOSITORY_ID = matcher.group(1); + matcher = Pattern.compile("repositoryName=(.+?),").matcher(content); + matcher.find(); + TestConstant.REPOSITORY_NAME = matcher.group(1); + System.out.println(TestConstant.REPOSITORY_ID); + System.out.println(TestConstant.REPOSITORY_NAME); + } + + @Test + @Order(Ordered.HIGHEST_PRECEDENCE + 1) + public void testUpdateRepositoryValid() throws Exception { + String newDescription = "This is a test description"; + mvc.perform( + post(ApiPathConstant.REPOSITORY_UPDATE_REPOSITORY_API_PATH) + .header(HeaderParameter.ACCESS_TOKEN, TestConstant.ACCESS_TOKEN) + .contentType(MediaType.APPLICATION_JSON) + .content( + """ + { + "id": "%s", + "repositoryDescription": "%s" + } + """.formatted(TestConstant.REPOSITORY_ID, newDescription))) + .andExpectAll( + status().isOk(), + jsonPath("$.id").value(TestConstant.REPOSITORY_ID), + jsonPath("$.repositoryName").value(TestConstant.REPOSITORY_NAME), + jsonPath("$.repositoryDescription").value(newDescription), + jsonPath("$.isPrivate").value(false), + jsonPath("$.userId").value(TestConstant.ID), + jsonPath("$.star").value(0), + jsonPath("$.fork").value(0), + jsonPath("$.watcher").value(0) + ); } + + @Test + @Order(Ordered.HIGHEST_PRECEDENCE + 2) + public void testDeleteRepositoryValid() throws Exception { + mvc.perform( + delete(ApiPathConstant.REPOSITORY_DELETE_REPOSITORY_API_PATH) + .header(HeaderParameter.ACCESS_TOKEN, TestConstant.ACCESS_TOKEN) + .param("id", TestConstant.REPOSITORY_ID)) + .andExpect(status().isOk()); + TestConstant.REPOSITORY_ID = null; + TestConstant.REPOSITORY_NAME = null; + TestConstant.REPOSITORY_SIZE--; + } + } diff --git a/src/test/java/edu/cmipt/gcs/controller/SshKeyControllerTest.java b/src/test/java/edu/cmipt/gcs/controller/SshKeyControllerTest.java index 85bf8bb..3ce6cd3 100644 --- a/src/test/java/edu/cmipt/gcs/controller/SshKeyControllerTest.java +++ b/src/test/java/edu/cmipt/gcs/controller/SshKeyControllerTest.java @@ -58,7 +58,7 @@ public void testUploadSshKeyValid() throws Exception { @Test @Order(Ordered.HIGHEST_PRECEDENCE + 1) public void testPageSshKeyValid() throws Exception { - var response = + var content = mockMvc.perform( get(ApiPathConstant.SSH_KEY_PAGE_SSH_KEY_API_PATH) .header( @@ -72,12 +72,13 @@ public void testPageSshKeyValid() throws Exception { jsonPath("$").isArray(), jsonPath("$.length()").value(TestConstant.SSH_KEY_SIZE)) .andReturn() - .getResponse(); + .getResponse() + .getContentAsString(); Matcher matcher = - Pattern.compile("id=(\\d+)") + Pattern.compile("id=(\\d+),") .matcher( JsonParserFactory.getJsonParser() - .parseList(response.getContentAsString()) + .parseList(content) .get(0) .toString()); matcher.find(); diff --git a/src/test/java/edu/cmipt/gcs/controller/UserControllerTest.java b/src/test/java/edu/cmipt/gcs/controller/UserControllerTest.java index f8ae5d5..77d0d9f 100644 --- a/src/test/java/edu/cmipt/gcs/controller/UserControllerTest.java +++ b/src/test/java/edu/cmipt/gcs/controller/UserControllerTest.java @@ -20,6 +20,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.json.JsonParserFactory; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.Ordered; @@ -27,6 +28,8 @@ import org.springframework.test.web.servlet.MockMvc; import java.util.Date; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Tests for UserController @@ -282,6 +285,6 @@ public void testPageOtherUserRepositoryValid() throws Exception { .andExpectAll( status().isOk(), jsonPath("$").isArray(), - jsonPath("$.length()").value(TestConstant.REPOSITORY_SIZE / 2)); + jsonPath("$.length()").value(TestConstant.REPOSITORY_SIZE)); } } From e9f5518981457854f592ed724efe69a1014d25bf Mon Sep 17 00:00:00 2001 From: Kaiser-Yang Date: Wed, 25 Sep 2024 11:20:27 +0000 Subject: [PATCH 2/2] Apply Google Java Style Format --- .../gcs/controller/RepositoryController.java | 86 ++++++++++--------- .../gcs/controller/SshKeyController.java | 10 ++- .../cmipt/gcs/service/UserServiceImpl.java | 9 +- .../controller/RepositoryControllerTest.java | 56 ++++++------ .../gcs/controller/UserControllerTest.java | 3 - 5 files changed, 90 insertions(+), 74 deletions(-) diff --git a/src/main/java/edu/cmipt/gcs/controller/RepositoryController.java b/src/main/java/edu/cmipt/gcs/controller/RepositoryController.java index b1d94f4..c82c7a4 100644 --- a/src/main/java/edu/cmipt/gcs/controller/RepositoryController.java +++ b/src/main/java/edu/cmipt/gcs/controller/RepositoryController.java @@ -13,6 +13,7 @@ import edu.cmipt.gcs.util.JwtUtil; import edu.cmipt.gcs.validation.group.CreateGroup; import edu.cmipt.gcs.validation.group.UpdateGroup; + import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; @@ -58,7 +59,9 @@ public void createRepository( @Validated(CreateGroup.class) @RequestBody RepositoryDTO repository, @RequestHeader(HeaderParameter.ACCESS_TOKEN) String accessToken) { if (repository.isPrivate() != null && repository.isPrivate()) { - throw new GenericException(ErrorCodeEnum.OPERATION_NOT_IMPLEMENTED, "private repository is not implemented"); + throw new GenericException( + ErrorCodeEnum.OPERATION_NOT_IMPLEMENTED, + "private repository is not implemented"); } String userId = JwtUtil.getId(accessToken); RepositoryPO repositoryPO = new RepositoryPO(repository, userId); @@ -75,25 +78,22 @@ public void createRepository( @DeleteMapping(ApiPathConstant.REPOSITORY_DELETE_REPOSITORY_API_PATH) @Operation( - summary = "Delete a repository", - description = "Delete a repository with the given id", - tags = {"Repository", "Delete Method"} - ) + summary = "Delete a repository", + description = "Delete a repository with the given id", + tags = {"Repository", "Delete Method"}) @Parameters({ @Parameter( - name = HeaderParameter.ACCESS_TOKEN, - description = "Access token", - required = true, - in = ParameterIn.HEADER, - schema = @Schema(implementation = String.class) - ), + name = HeaderParameter.ACCESS_TOKEN, + description = "Access token", + required = true, + in = ParameterIn.HEADER, + schema = @Schema(implementation = String.class)), @Parameter( - name = "id", - description = "Repository id", - required = true, - in = ParameterIn.QUERY, - schema = @Schema(implementation = Long.class) - ) + name = "id", + description = "Repository id", + required = true, + in = ParameterIn.QUERY, + schema = @Schema(implementation = Long.class)) }) @ApiResponses({ @ApiResponse(responseCode = "200", description = "Repository deleted successfully"), @@ -101,16 +101,18 @@ public void createRepository( @ApiResponse(responseCode = "404", description = "Repository not found") }) public void deleteRepository( - @RequestHeader(HeaderParameter.ACCESS_TOKEN) String accessToken, - @RequestParam("id") Long id - ) { + @RequestHeader(HeaderParameter.ACCESS_TOKEN) String accessToken, + @RequestParam("id") Long id) { var repository = repositoryService.getById(id); if (repository == null) { throw new GenericException(ErrorCodeEnum.REPOSITORY_NOT_FOUND, id); } String userId = JwtUtil.getId(accessToken); if (!userId.equals(repository.getUserId().toString())) { - logger.info("User[{}] tried to delete repository of user[{}]", userId, repository.getUserId()); + logger.info( + "User[{}] tried to delete repository of user[{}]", + userId, + repository.getUserId()); throw new GenericException(ErrorCodeEnum.ACCESS_DENIED); } if (!repositoryService.removeById(id)) { @@ -120,27 +122,26 @@ public void deleteRepository( @PostMapping(ApiPathConstant.REPOSITORY_UPDATE_REPOSITORY_API_PATH) @Operation( - summary = "Update a repository", - description = "Update a repository with the given information", - tags = {"Repository", "Post Method"} - ) + summary = "Update a repository", + description = "Update a repository with the given information", + tags = {"Repository", "Post Method"}) @Parameter( - name = HeaderParameter.ACCESS_TOKEN, - description = "Access token", - required = true, - in = ParameterIn.HEADER, - schema = @Schema(implementation = String.class) - ) + name = HeaderParameter.ACCESS_TOKEN, + description = "Access token", + required = true, + in = ParameterIn.HEADER, + schema = @Schema(implementation = String.class)) @ApiResponses({ @ApiResponse(responseCode = "200", description = "Repository updated successfully"), @ApiResponse(responseCode = "403", description = "Access denied"), @ApiResponse(responseCode = "404", description = "Repository not found"), - @ApiResponse(responseCode = "501", description = "Update repository name is not implemented") + @ApiResponse( + responseCode = "501", + description = "Update repository name is not implemented") }) public ResponseEntity updateRepository( - @Validated(UpdateGroup.class) @RequestBody RepositoryDTO repository, - @RequestHeader(HeaderParameter.ACCESS_TOKEN) String accessToken - ) { + @Validated(UpdateGroup.class) @RequestBody RepositoryDTO repository, + @RequestHeader(HeaderParameter.ACCESS_TOKEN) String accessToken) { Long id = null; try { id = Long.valueOf(repository.id()); @@ -154,12 +155,19 @@ public ResponseEntity updateRepository( } String userId = JwtUtil.getId(accessToken); if (!userId.equals(repositoryPO.getUserId().toString())) { - logger.info("User[{}] tried to update repository of user[{}]", userId, repositoryPO.getUserId()); + logger.info( + "User[{}] tried to update repository of user[{}]", + userId, + repositoryPO.getUserId()); throw new GenericException(ErrorCodeEnum.ACCESS_DENIED); } - if (repository.repositoryName() != null && - !repository.repositoryName().equals(repositoryService.getById(id).getRepositoryName())) { - throw new GenericException(ErrorCodeEnum.OPERATION_NOT_IMPLEMENTED, "update repository name is not implemented"); + if (repository.repositoryName() != null + && !repository + .repositoryName() + .equals(repositoryService.getById(id).getRepositoryName())) { + throw new GenericException( + ErrorCodeEnum.OPERATION_NOT_IMPLEMENTED, + "update repository name is not implemented"); } if (!repositoryService.updateById(new RepositoryPO(repository))) { throw new GenericException(ErrorCodeEnum.REPOSITORY_UPDATE_FAILED, repository); diff --git a/src/main/java/edu/cmipt/gcs/controller/SshKeyController.java b/src/main/java/edu/cmipt/gcs/controller/SshKeyController.java index 020046f..2916660 100644 --- a/src/main/java/edu/cmipt/gcs/controller/SshKeyController.java +++ b/src/main/java/edu/cmipt/gcs/controller/SshKeyController.java @@ -107,7 +107,10 @@ public void deleteSshKey( } String idInToken = JwtUtil.getId(accessToken); if (!idInToken.equals(sshKeyPO.getUserId().toString())) { - logger.info("User[{}] tried to delete SSH key of user[{}]", idInToken, sshKeyPO.getUserId()); + logger.info( + "User[{}] tried to delete SSH key of user[{}]", + idInToken, + sshKeyPO.getUserId()); throw new GenericException(ErrorCodeEnum.ACCESS_DENIED); } if (!sshKeyService.removeById(id)) { @@ -151,7 +154,10 @@ public ResponseEntity updateSshKey( } String idInToken = JwtUtil.getId(accessToken); if (!idInToken.equals(sshKeyPO.getUserId().toString())) { - logger.info("User[{}] tried to update SSH key of user[{}]", idInToken, sshKeyPO.getUserId()); + logger.info( + "User[{}] tried to update SSH key of user[{}]", + idInToken, + sshKeyPO.getUserId()); throw new GenericException(ErrorCodeEnum.ACCESS_DENIED); } if (!sshKeyService.updateById(new SshKeyPO(sshKeyDTO))) { diff --git a/src/main/java/edu/cmipt/gcs/service/UserServiceImpl.java b/src/main/java/edu/cmipt/gcs/service/UserServiceImpl.java index 95a30f3..da1f473 100644 --- a/src/main/java/edu/cmipt/gcs/service/UserServiceImpl.java +++ b/src/main/java/edu/cmipt/gcs/service/UserServiceImpl.java @@ -4,10 +4,8 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import edu.cmipt.gcs.dao.UserMapper; -import edu.cmipt.gcs.pojo.user.UserPO; import edu.cmipt.gcs.pojo.ssh.SshKeyPO; - -import java.io.Serializable; +import edu.cmipt.gcs.pojo.user.UserPO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,12 +13,13 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.io.Serializable; + @Service public class UserServiceImpl extends ServiceImpl implements UserService { private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class); - @Autowired - SshKeyService sshKeyService; + @Autowired SshKeyService sshKeyService; @Override @Transactional diff --git a/src/test/java/edu/cmipt/gcs/controller/RepositoryControllerTest.java b/src/test/java/edu/cmipt/gcs/controller/RepositoryControllerTest.java index 359d88d..1c73609 100644 --- a/src/test/java/edu/cmipt/gcs/controller/RepositoryControllerTest.java +++ b/src/test/java/edu/cmipt/gcs/controller/RepositoryControllerTest.java @@ -6,9 +6,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - import edu.cmipt.gcs.constant.ApiPathConstant; import edu.cmipt.gcs.constant.HeaderParameter; import edu.cmipt.gcs.constant.TestConstant; @@ -25,6 +22,9 @@ import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * Tests for RepositoryController * @@ -55,17 +55,22 @@ public void testCreateRepositoryValid() throws Exception { .formatted(repositoryName))) .andExpect(status().isOk()); } - var content = + var content = mvc.perform( - get(ApiPathConstant.USER_PAGE_USER_REPOSITORY_API_PATH) - .header(HeaderParameter.ACCESS_TOKEN, TestConstant.ACCESS_TOKEN) - .param("id", TestConstant.ID) - .param("page", "1") - .param("size", TestConstant.REPOSITORY_SIZE.toString())) - .andExpectAll( - status().isOk(), - jsonPath("$").isArray(), - jsonPath("$.length()").value(TestConstant.REPOSITORY_SIZE)).andReturn().getResponse().getContentAsString(); + get(ApiPathConstant.USER_PAGE_USER_REPOSITORY_API_PATH) + .header( + HeaderParameter.ACCESS_TOKEN, + TestConstant.ACCESS_TOKEN) + .param("id", TestConstant.ID) + .param("page", "1") + .param("size", TestConstant.REPOSITORY_SIZE.toString())) + .andExpectAll( + status().isOk(), + jsonPath("$").isArray(), + jsonPath("$.length()").value(TestConstant.REPOSITORY_SIZE)) + .andReturn() + .getResponse() + .getContentAsString(); content = JsonParserFactory.getJsonParser().parseList(content).get(0).toString(); Matcher matcher = Pattern.compile("id=(\\d+),").matcher(content); matcher.find(); @@ -91,18 +96,20 @@ public void testUpdateRepositoryValid() throws Exception { "id": "%s", "repositoryDescription": "%s" } - """.formatted(TestConstant.REPOSITORY_ID, newDescription))) + """ + .formatted( + TestConstant.REPOSITORY_ID, + newDescription))) .andExpectAll( - status().isOk(), - jsonPath("$.id").value(TestConstant.REPOSITORY_ID), - jsonPath("$.repositoryName").value(TestConstant.REPOSITORY_NAME), - jsonPath("$.repositoryDescription").value(newDescription), - jsonPath("$.isPrivate").value(false), - jsonPath("$.userId").value(TestConstant.ID), - jsonPath("$.star").value(0), - jsonPath("$.fork").value(0), - jsonPath("$.watcher").value(0) - ); + status().isOk(), + jsonPath("$.id").value(TestConstant.REPOSITORY_ID), + jsonPath("$.repositoryName").value(TestConstant.REPOSITORY_NAME), + jsonPath("$.repositoryDescription").value(newDescription), + jsonPath("$.isPrivate").value(false), + jsonPath("$.userId").value(TestConstant.ID), + jsonPath("$.star").value(0), + jsonPath("$.fork").value(0), + jsonPath("$.watcher").value(0)); } @Test @@ -117,5 +124,4 @@ public void testDeleteRepositoryValid() throws Exception { TestConstant.REPOSITORY_NAME = null; TestConstant.REPOSITORY_SIZE--; } - } diff --git a/src/test/java/edu/cmipt/gcs/controller/UserControllerTest.java b/src/test/java/edu/cmipt/gcs/controller/UserControllerTest.java index 77d0d9f..1d821cb 100644 --- a/src/test/java/edu/cmipt/gcs/controller/UserControllerTest.java +++ b/src/test/java/edu/cmipt/gcs/controller/UserControllerTest.java @@ -20,7 +20,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.json.JsonParserFactory; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.Ordered; @@ -28,8 +27,6 @@ import org.springframework.test.web.servlet.MockMvc; import java.util.Date; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * Tests for UserController