Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an API for getting the repository list by user id #47

Merged
merged 3 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading