Skip to content

Commit

Permalink
Merge pull request #47 from CMIPT/kaiser-crud
Browse files Browse the repository at this point in the history
Add an API for getting the repository list by user id.
  • Loading branch information
Kaiser-Yang authored Sep 19, 2024
2 parents 0701b7c + de2fd95 commit 6467f70
Show file tree
Hide file tree
Showing 12 changed files with 257 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/main/java/edu/cmipt/gcs/constant/ApiPathConstant.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@ public class ApiPathConstant {
public static final String USER_CHECK_USERNAME_VALIDITY_API_PATH =
USER_API_PREFIX + "/username";
public static final String USER_DELETE_USER_API_PATH = USER_API_PREFIX + "/delete";
public static final String USER_PAGE_USER_REPOSITORY_API_PATH =
USER_API_PREFIX + "/page/repository";
}
6 changes: 6 additions & 0 deletions src/main/java/edu/cmipt/gcs/constant/ValidationConstant.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,10 @@ public class ValidationConstant {
// so we just use '*' to ignore the length check
public static final String USERNAME_PATTERN = "^[a-zA-Z0-9_]*$";
public static final String PASSWORD_PATTERN = "^[a-zA-Z0-9_.@]*$";
public static final int MIN_REPOSITORY_NAME_LENGTH = 1;
public static final int MAX_REPOSITORY_NAME_LENGTH = 255;
public static final int MIN_REPOSITORY_DESCRIPTION_LENGTH = 0;
public static final int MAX_REPOSITORY_DESCRIPTION_LENGTH = 255;
// the length will be checked by @Size
public static final String REPOSITORY_NAME_PATTERN = "^[a-zA-Z0-9_-]*$";
}
62 changes: 62 additions & 0 deletions src/main/java/edu/cmipt/gcs/controller/UserController.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package edu.cmipt.gcs.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import edu.cmipt.gcs.constant.ApiPathConstant;
import edu.cmipt.gcs.constant.HeaderParameter;
import edu.cmipt.gcs.constant.ValidationConstant;
import edu.cmipt.gcs.enumeration.ErrorCodeEnum;
import edu.cmipt.gcs.exception.GenericException;
import edu.cmipt.gcs.pojo.error.ErrorVO;
import edu.cmipt.gcs.pojo.repository.RepositoryPO;
import edu.cmipt.gcs.pojo.repository.RepositoryVO;
import edu.cmipt.gcs.pojo.user.UserDTO;
import edu.cmipt.gcs.pojo.user.UserPO;
import edu.cmipt.gcs.pojo.user.UserVO;
import edu.cmipt.gcs.service.RepositoryService;
import edu.cmipt.gcs.service.UserService;
import edu.cmipt.gcs.util.JwtUtil;
import edu.cmipt.gcs.validation.group.UpdateGroup;
Expand Down Expand Up @@ -41,11 +45,15 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.stream.Collectors;

@Validated
@RestController
@Tag(name = "User", description = "User Related APIs")
public class UserController {
@Autowired private UserService userService;
@Autowired private RepositoryService repositoryService;

@GetMapping(ApiPathConstant.USER_GET_USER_API_PATH)
@Operation(
Expand Down Expand Up @@ -180,6 +188,60 @@ public void deleteUser(
JwtUtil.blacklistToken(accessToken, refreshToken);
}

@GetMapping(ApiPathConstant.USER_PAGE_USER_REPOSITORY_API_PATH)
@Operation(
summary = "Page user repositories",
description =
"Page user repositories. If the given token is trying to get other's"
+ " repositories, only public repositories will be shown",
tags = {"User", "Get Method"})
@Parameters({
@Parameter(
name = HeaderParameter.ACCESS_TOKEN,
description = "Access token",
required = true,
in = ParameterIn.HEADER,
schema = @Schema(implementation = String.class)),
@Parameter(
name = "id",
description = "User id",
required = true,
in = ParameterIn.QUERY,
schema = @Schema(implementation = Long.class)),
@Parameter(
name = "page",
description = "Page number",
example = "1",
required = true,
in = ParameterIn.QUERY,
schema = @Schema(implementation = Integer.class)),
@Parameter(
name = "size",
description = "Page size",
example = "10",
required = true,
in = ParameterIn.QUERY,
schema = @Schema(implementation = Integer.class))
})
@ApiResponse(responseCode = "200", description = "User repositories paged successfully")
public List<RepositoryVO> pageUserRepositories(
@RequestParam("id") Long userId,
@RequestParam("page") Integer page,
@RequestParam("size") Integer size,
@RequestHeader(HeaderParameter.ACCESS_TOKEN) String accessToken) {
QueryWrapper<RepositoryPO> wrapper = new QueryWrapper<RepositoryPO>();
String idInToken = JwtUtil.getID(accessToken);
assert idInToken != null;
if (!idInToken.equals(userId.toString())) {
// the user only can see the public repositories of others
wrapper.eq("is_private", false);
}
wrapper.eq("user_id", userId);
return repositoryService.page(new Page<>(page, size), wrapper).getRecords().stream()
.map(RepositoryVO::new)
.collect(Collectors.toList());
}

@GetMapping(ApiPathConstant.USER_CHECK_EMAIL_VALIDITY_API_PATH)
@Operation(
summary = "Check email validity",
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/edu/cmipt/gcs/dao/RepositoryMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package edu.cmipt.gcs.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

import edu.cmipt.gcs.pojo.repository.RepositoryPO;

public interface RepositoryMapper extends BaseMapper<RepositoryPO> {}
13 changes: 12 additions & 1 deletion src/main/java/edu/cmipt/gcs/enumeration/ErrorCodeEnum.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,18 @@ public enum ErrorCodeEnum {
USER_NOT_FOUND("USER_NOT_FOUND"),

USER_UPDATE_FAILED("USER_UPDATE_FAILED"),
USER_DELETE_FAILED("USER_DELETE_FAILED");
USER_DELETE_FAILED("USER_DELETE_FAILED"),

REPOSITORYDTO_ID_NULL("RepositoryDTO.id.Null"),
REPOSITORYDTO_ID_NOTNULL("RepositoryDTO.id.NotNull"),
REPOSITORYDTO_REPOSITORYNAME_SIZE("RepositoryDTO.repositoryName.Size"),
REPOSITORYDTO_REPOSITORYNAME_NOTBLANK("RepositoryDTO.repositoryName.NotBlank"),
REPOSITORYDTO_REPOSITORYDESCRIPTION_SIZE("RepositoryDTO.repositoryDescription.Size"),
REPOSITORYDTO_STAR_MIN("RepositoryDTO.star.Min"),
REPOSITORYDTO_FORK_MIN("RepositoryDTO.fork.Min"),
REPOSITORYDTO_WATCHER_MIN("RepositoryDTO.watcher.Min"),

REPOSITORYNAME_PATTERN_MISMATCH("REPOSITORYNAME_PATTERN_MISMATCH");

// code means the error code in the message.properties
private String code;
Expand Down
75 changes: 75 additions & 0 deletions src/main/java/edu/cmipt/gcs/pojo/repository/RepositoryDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package edu.cmipt.gcs.pojo.repository;

import edu.cmipt.gcs.constant.ValidationConstant;
import edu.cmipt.gcs.validation.group.CreateGroup;
import edu.cmipt.gcs.validation.group.UpdateGroup;

import io.swagger.v3.oas.annotations.media.Schema;

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Null;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;

@Schema(description = "Repository Data Transfer Object")
public record RepositoryDTO(
@Schema(description = "Repository ID")
@Null(
groups = CreateGroup.class,
message = "REPOSITORYDTO_ID_NULL {RepositoryDTO.id.Null}")
@NotNull(
groups = UpdateGroup.class,
message = "REPOSITORYDTO_ID_NOTNULL {RepositoryDTO.id.NotNull}")
String id,
@Schema(
description = "Repository Name",
requiredMode = Schema.RequiredMode.REQUIRED,
example = "gcs")
@Size(
groups = {CreateGroup.class, UpdateGroup.class},
min = ValidationConstant.MIN_REPOSITORY_NAME_LENGTH,
max = ValidationConstant.MAX_REPOSITORY_NAME_LENGTH,
message =
"REPOSITORYDTO_REPOSITORYNAME_SIZE"
+ " {RepositoryDTO.repositoryName.Size}")
@NotBlank(
groups = {CreateGroup.class},
message =
"REPOSITORYDTO_REPOSITORYNAME_NOTBLANK"
+ " {RepositoryDTO.repositoryName.NotBlank}")
@Pattern(
regexp = ValidationConstant.REPOSITORY_NAME_PATTERN,
groups = {CreateGroup.class, UpdateGroup.class},
message =
"REPOSITORYNAME_PATTERN_MISMATCH {REPOSITORYNAME_PATTERN_MISMATCH}")
String repositoryName,
@Schema(description = "Repository Description")
@Size(
groups = {CreateGroup.class, UpdateGroup.class},
min = ValidationConstant.MIN_REPOSITORY_DESCRIPTION_LENGTH,
max = ValidationConstant.MAX_REPOSITORY_DESCRIPTION_LENGTH,
message =
"REPOSITORYDTO_REPOSITORYDESCRIPTION_SIZE"
+ " {RepositoryDTO.repositoryDescription.Size}")
String repositoryDescription,
@Schema(description = "Whether or Not Private Repo") Boolean isPrivate,
@Schema(description = "Star Count")
@Min(
groups = {CreateGroup.class, UpdateGroup.class},
value = 0,
message = "REPOSITORYDTO_STAR_MIN {RepositoryDTO.star.Min}")
Integer star,
@Schema(description = "Fork Count")
@Min(
groups = {CreateGroup.class, UpdateGroup.class},
value = 0,
message = "REPOSITORYDTO_FORK_MIN {RepositoryDTO.fork.Min}")
Integer fork,
@Schema(description = "Watcher Count")
@Min(
groups = {CreateGroup.class, UpdateGroup.class},
value = 0,
message = "REPOSITORYDTO_WATCHER_MIN {RepositoryDTO.watcher.Min}")
Integer watcher) {}
39 changes: 39 additions & 0 deletions src/main/java/edu/cmipt/gcs/pojo/repository/RepositoryPO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package edu.cmipt.gcs.pojo.repository;

import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;

import lombok.Data;

import java.time.LocalDateTime;

@Data
@TableName("t_repository")
public class RepositoryPO {
private Long id;
private String repositoryName;
private String repositoryDescription;
private Boolean isPrivate;
private Long userId;
private Integer star;
private Integer fork;
private Integer watcher;
private LocalDateTime gmtCreated;
private LocalDateTime gmtUpdated;
@TableLogic private LocalDateTime gmtDeleted;

public RepositoryPO(RepositoryDTO repositoryDTO, Long userId) {
try {
this.id = Long.valueOf(repositoryDTO.id());
} catch (NumberFormatException e) {
this.id = null;
}
this.repositoryName = repositoryDTO.repositoryName();
this.repositoryDescription = repositoryDTO.repositoryDescription();
this.isPrivate = repositoryDTO.isPrivate();
this.userId = userId;
this.star = repositoryDTO.star();
this.fork = repositoryDTO.fork();
this.watcher = repositoryDTO.watcher();
}
}
23 changes: 23 additions & 0 deletions src/main/java/edu/cmipt/gcs/pojo/repository/RepositoryVO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package edu.cmipt.gcs.pojo.repository;

import io.swagger.v3.oas.annotations.media.Schema;

public record RepositoryVO(
@Schema(description = "Repository ID") String id,
@Schema(description = "Repository Name") String repositoryName,
@Schema(description = "Repository Description") String repositoryDescription,
@Schema(description = "Whether or Not Private Repo") Boolean isPrivate,
@Schema(description = "Star Count") Integer star,
@Schema(description = "Fork Count") Integer fork,
@Schema(description = "Watcher Count") Integer watcher) {
public RepositoryVO(RepositoryPO repositoryPO) {
this(
repositoryPO.getId().toString(),
repositoryPO.getRepositoryName(),
repositoryPO.getRepositoryDescription(),
repositoryPO.getIsPrivate(),
repositoryPO.getStar(),
repositoryPO.getFork(),
repositoryPO.getWatcher());
}
}
2 changes: 1 addition & 1 deletion src/main/java/edu/cmipt/gcs/pojo/user/UserDTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*/
@Schema(description = "User Data Transfer Object")
public record UserDTO(
@Schema(description = "User ID", accessMode = Schema.AccessMode.READ_ONLY)
@Schema(description = "User ID")
@Null(groups = CreateGroup.class, message = "USERDTO_ID_NULL {UserDTO.id.Null}")
@NotNull(
groups = UpdateGroup.class,
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/edu/cmipt/gcs/service/RepositoryService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package edu.cmipt.gcs.service;

import com.baomidou.mybatisplus.extension.service.IService;

import edu.cmipt.gcs.pojo.repository.RepositoryPO;

public interface RepositoryService extends IService<RepositoryPO> {}
12 changes: 12 additions & 0 deletions src/main/java/edu/cmipt/gcs/service/RepositoryServiceImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package edu.cmipt.gcs.service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import edu.cmipt.gcs.dao.RepositoryMapper;
import edu.cmipt.gcs.pojo.repository.RepositoryPO;

import org.springframework.stereotype.Service;

@Service
public class RepositoryServiceImpl extends ServiceImpl<RepositoryMapper, RepositoryPO>
implements RepositoryService {}
11 changes: 11 additions & 0 deletions src/main/resources/message/message.properties
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,14 @@ USER_NOT_FOUND=User not found: {}

USER_UPDATE_FAILED=User update failed: {}
USER_DELETE_FAILED=User delete failed: {}

RepositoryDTO.id.Null=Repository id must be null when creating a new repository
RepositoryDTO.id.NotNull=Repository id cannot be null
RepositoryDTO.name.Size=Repository name must be between {min} and {max} characters
RepositoryDTO.name.NotBlank=Repository name cannot be blank
RepositoryDTO.description.Size=Repository description must be between {min} and {max} characters
RepositoryDTO.star.Min=Star must be greater than or equal to {value}
RepositoryDTO.fork.Min=Fork must be greater than or equal to {value}
RepositoryDTO.watcher.Min=Watcher must be greater than or equal to {value}

REPOSITORYNAME_PATTERN_MISMATCH=Repository name can only be alphanumeric, hyphen or underline

0 comments on commit 6467f70

Please sign in to comment.