Skip to content

Commit

Permalink
✨ feat: get pull request (related: #37)
Browse files Browse the repository at this point in the history
  • Loading branch information
Zerohertz committed Jan 29, 2025
1 parent 6f8d511 commit 9477c55
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.server.domain.pull.controller;

import java.util.List;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.server.domain.pull.dto.GetPullRequestInfoOutDto;
import com.server.domain.pull.dto.GetPullRequestOutDto;
import com.server.domain.pull.enums.PullRequestState;
import com.server.domain.pull.service.PullRequestService;
import com.server.global.dto.ApiResponseDto;

import io.swagger.v3.oas.annotations.Operation;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/pulls")
public class PullRequestController {

private final PullRequestService pullRequestService;

@ResponseStatus(HttpStatus.OK)
@GetMapping("/{userOrOrgName}/{repositoryName}")
@Operation(summary = "레포지토리에 따른 pull requests 조회", description = "- all, open, closed의 상태로 filtering 가능</br>- Merge 완료된 pull request는 merged_at 시간 존재")
public ApiResponseDto<List<GetPullRequestOutDto>> getPullRequests(HttpServletRequest request,
@PathVariable String userOrOrgName, @PathVariable String repositoryName,
@RequestParam(required = false) PullRequestState state) {
return ApiResponseDto.success(HttpStatus.OK.value(),
pullRequestService.getPullRequests(request, userOrOrgName, repositoryName, state));
}

@ResponseStatus(HttpStatus.OK)
@GetMapping("/{userOrOrgName}/{repositoryName}/{pullNo}")
@Operation(summary = "", description = "")
public ApiResponseDto<GetPullRequestInfoOutDto> getPullRequests(HttpServletRequest request,
@PathVariable String userOrOrgName, @PathVariable String repositoryName, @PathVariable int pullNo) {
return ApiResponseDto.success(HttpStatus.OK.value(),
pullRequestService.getPullRequest(request, userOrOrgName, repositoryName, pullNo));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.server.domain.pull.dto;

import com.fasterxml.jackson.annotation.JsonProperty;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class GetPullRequestInfoOutDto extends PullRequestDto {

@JsonProperty("merged")
Boolean merged;

@JsonProperty("mergeable")
Boolean mergeable;

@JsonProperty("mergeable_state")
String mergeableState;

// NOTE: 일반 댓글 (comments)는 고려하지 않고 리뷰 댓글 (review_comments)만 고려
@JsonProperty("review_comments")
int comments;

@JsonProperty("commits")
int commits;

@JsonProperty("additions")
int additions;

@JsonProperty("deletions")
int deletions;

@JsonProperty("changed_files")
int changedFiles;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.server.domain.pull.dto;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class GetPullRequestOutDto extends PullRequestDto {
}
48 changes: 48 additions & 0 deletions src/main/java/com/server/domain/pull/dto/PullRequestDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.server.domain.pull.dto;

import java.time.LocalDateTime;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.server.domain.pull.enums.PullRequestState;

import lombok.Getter;

@Getter
public class PullRequestDto {

@JsonProperty("number")
private Integer prId;

@JsonProperty("html_url")
private String url;

@JsonProperty("user")
private void unpackUser(JsonNode user) {
this.ownerId = user.get("login").asText();
}

private String ownerId;

@JsonProperty("title")
private String title;

@JsonProperty("head")
private void unpackHead(JsonNode head) {
this.branchName = head.get("ref").asText();
}

private String branchName;

@JsonProperty("state")
private PullRequestState status;

@JsonProperty("created_at")
private LocalDateTime createdAt;

@JsonProperty("closed_at")
private LocalDateTime closedAt;

@JsonProperty("merged_at")
private LocalDateTime mergedAt;
}
33 changes: 33 additions & 0 deletions src/main/java/com/server/domain/pull/enums/PullRequestState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.server.domain.pull.enums;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonValue;

@JsonFormat(shape = JsonFormat.Shape.STRING)
public enum PullRequestState {
ALL("all"),
OPEN("open"),
CLOSED("closed");

private final String value;

PullRequestState(String value) {
this.value = value;
}

@JsonValue
public String getValue() {
return value;
}

@JsonCreator
public static PullRequestState fromValue(String value) {
for (PullRequestState status : values()) {
if (status.value.equalsIgnoreCase(value)) {
return status;
}
}
throw new IllegalArgumentException("Invalid status: " + value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.server.domain.pull.enums;

import java.beans.PropertyEditorSupport;

// NOTE: Controller에서 소문자로 입력되어도 enum type으로 인식하기 위하여 필요
public class PullRequestStateEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(PullRequestState.fromValue(text));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.server.domain.pull.service;

import java.util.List;

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import com.server.domain.pull.dto.GetPullRequestInfoOutDto;
import com.server.domain.pull.dto.GetPullRequestOutDto;
import com.server.domain.pull.enums.PullRequestState;
import com.server.domain.user.entity.User;
import com.server.domain.user.service.UserService;
import com.server.global.error.code.AuthErrorCode;
import com.server.global.error.exception.AuthException;
import com.server.global.jwt.JwtService;

import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Service
@RequiredArgsConstructor
@Slf4j
public class PullRequestService {

private final UserService userService;
private final JwtService jwtService;

public List<GetPullRequestOutDto> getPullRequests(HttpServletRequest request, String userOrOrgName,
String repositoryName, PullRequestState state) {
String username = jwtService.extractUsernameFromToken(request)
.orElseThrow(() -> new AuthException(AuthErrorCode.INVALID_ACCESS_TOKEN));
User user = userService.getUserWithPersonalInfo(username);

RestTemplate restTemplate = new RestTemplate();
String url = String.format("https://api.github.com/repos/%s/%s/pulls", userOrOrgName, repositoryName);
if (state != null) {
url = String.format("%s?state=%s", url, state.getValue());
}
ResponseEntity<List<GetPullRequestOutDto>> githubPullRequests = restTemplate.exchange(
url,
HttpMethod.GET,
buildGithubHttpEntity(user.getGithubToken()),
new ParameterizedTypeReference<List<GetPullRequestOutDto>>() {
});
if (githubPullRequests.getStatusCode().value() != 200) {
throw new AuthException(AuthErrorCode.OAUTH_PROCESS_ERROR);
}
return githubPullRequests.getBody();
}

public GetPullRequestInfoOutDto getPullRequest(HttpServletRequest request, String userOrOrgName,
String repositoryName, int pullNo) {
String username = jwtService.extractUsernameFromToken(request)
.orElseThrow(() -> new AuthException(AuthErrorCode.INVALID_ACCESS_TOKEN));
User user = userService.getUserWithPersonalInfo(username);

RestTemplate restTemplate = new RestTemplate();
String url = String.format("https://api.github.com/repos/%s/%s/pulls/%s", userOrOrgName, repositoryName,
pullNo);
ResponseEntity<GetPullRequestInfoOutDto> githubPullRequest = restTemplate.exchange(
url,
HttpMethod.GET,
buildGithubHttpEntity(user.getGithubToken()),
GetPullRequestInfoOutDto.class);
return githubPullRequest.getBody();
}

// TODO: 아래 endpoint에서 어떤 파일들이 어떻게 변경 되었는지 파악할 수 있음
// String url =
// String.format("https://api.github.com/repos/%s/%s/pulls/%s/files",
// userOrOrgName, repositoryName,
// pullNo);

private HttpEntity<MultiValueMap<String, String>> buildGithubHttpEntity(String accessToken) {
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
HttpHeaders headers = new HttpHeaders();
headers.add("Accept", "application/vnd.github+json");
headers.add("X-GitHub-Api-Version", "2022-11-28");
headers.setBearerAuth(accessToken);
return new HttpEntity<>(params, headers);
}
}

This file was deleted.

This file was deleted.

0 comments on commit 9477c55

Please sign in to comment.