Skip to content

Commit 7cfb784

Browse files
authored
Merge pull request #118 from Capstone-OpenStep/feature/#111-issue-detail-by-url
Feat: 로그인한 사용자의 프로필 조회 API 구현
2 parents ded1b53 + 8a12623 commit 7cfb784

9 files changed

Lines changed: 164 additions & 5 deletions

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.chungang.capstone.openstep.domain.Github.dto;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
import lombok.Data;
6+
7+
import java.util.Map;
8+
9+
@Data
10+
@JsonIgnoreProperties(ignoreUnknown = true)
11+
public class GitHubUserProfile {
12+
13+
private String login;
14+
//private String name;
15+
private String email;
16+
private String avatarUrl;
17+
private String location;
18+
private String url;
19+
20+
private int followersCount;
21+
private int followingCount;
22+
23+
@JsonProperty("followers")
24+
private void unpackFollowers(Map<String, Integer> followers) {
25+
this.followersCount = followers.get("totalCount");
26+
}
27+
28+
@JsonProperty("following")
29+
private void unpackFollowing(Map<String, Integer> following) {
30+
this.followingCount = following.get("totalCount");
31+
}
32+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.chungang.capstone.openstep.domain.Github.dto;
2+
3+
import lombok.Data;
4+
5+
@Data
6+
public class GitHubUserProfileResponse {
7+
private ViewerData data;
8+
9+
@Data
10+
public static class ViewerData {
11+
private GitHubUserProfile viewer;
12+
}
13+
}

src/main/java/com/chungang/capstone/openstep/domain/Github/service/GitHubGraphQLService.java

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22

33
import java.util.List;
44
import java.util.Objects;
5+
import java.util.Optional;
56

6-
import com.chungang.capstone.openstep.domain.Github.dto.GitHubGraphQLRequest;
7-
import com.chungang.capstone.openstep.domain.Github.dto.GitHubIssueResponse;
8-
import com.chungang.capstone.openstep.domain.Github.dto.GitHubRepoResponse;
9-
import com.chungang.capstone.openstep.domain.Github.dto.PullRequestResponse;
10-
import com.chungang.capstone.openstep.domain.Github.dto.PullRequestResponseWrapper;
7+
import com.chungang.capstone.openstep.domain.Github.dto.*;
118
import com.chungang.capstone.openstep.global.apiPayload.code.status.ErrorStatus;
129
import com.chungang.capstone.openstep.global.apiPayload.exception.GithubGraphQLException;
1310

@@ -400,5 +397,44 @@ public GitHubIssueResponse.IssueNode fetchIssueByUrl(String url) {
400397
}
401398

402399

400+
public GitHubUserProfile fetchAuthenticatedUserProfile(String accessToken) {
401+
String query = """
402+
{
403+
viewer {
404+
login
405+
avatarUrl
406+
email
407+
location
408+
followers { totalCount }
409+
following { totalCount }
410+
url
411+
}
412+
}
413+
""";
414+
415+
HttpHeaders headers = new HttpHeaders();
416+
headers.setContentType(MediaType.APPLICATION_JSON);
417+
headers.setBearerAuth(accessToken);
418+
419+
HttpEntity<GitHubGraphQLRequest> request = new HttpEntity<>(new GitHubGraphQLRequest(query), headers);
420+
421+
try {
422+
ResponseEntity<GitHubUserProfileResponse> response = restTemplate.exchange(
423+
GITHUB_GRAPHQL_URL, HttpMethod.POST, request, GitHubUserProfileResponse.class
424+
);
425+
426+
return Optional.ofNullable(response.getBody())
427+
.map(GitHubUserProfileResponse::getData)
428+
.map(GitHubUserProfileResponse.ViewerData::getViewer)
429+
.orElseThrow(() -> new GithubGraphQLException(ErrorStatus.GITHUB_GRAPHQL_ERROR));
430+
431+
} catch (Exception e) {
432+
log.error("GitHub 프로필 정보 조회 실패", e);
433+
throw new GithubGraphQLException(ErrorStatus.GITHUB_GRAPHQL_ERROR);
434+
}
435+
}
436+
437+
438+
403439

404440
}

src/main/java/com/chungang/capstone/openstep/domain/Member/controller/MemberController.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import java.util.List;
44

5+
import com.chungang.capstone.openstep.domain.Github.dto.GitHubUserProfile;
6+
import com.chungang.capstone.openstep.domain.Member.converter.MemberConverter;
7+
import com.chungang.capstone.openstep.domain.Member.entity.Member;
58
import com.chungang.capstone.openstep.domain.Member.service.AuthService;
69
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
710
import jakarta.validation.Valid;
@@ -124,4 +127,29 @@ public ApiResponse<List<PullRequestResponse.PullRequestRes>> getContributions(){
124127
List<PullRequestResponse.PullRequestRes> contributions = gitHubGraphQLService.fetchMyPullRequestsWithIssues(githubId);
125128
return ApiResponse.onSuccess(SuccessStatus.ISSUE_GET_DETAIL_OK, contributions);
126129
}
130+
131+
@GetMapping("/github/profile/realtime")
132+
@Operation(summary = "GitHub 프로필 실시간 조회", description = "GitHub GraphQL API를 통해 실시간으로 프로필 정보를 조회합니다.")
133+
public ApiResponse<MemberResponseDTO.GitHubProfileDTO> getGitHubProfileRealtime() {
134+
Long memberId = SecurityUtils.getCurrentMemberId();
135+
Member member = memberQueryService.getMemberById(memberId);
136+
String accessToken = member.getGithubAccessToken();
137+
138+
GitHubUserProfile profile = gitHubGraphQLService.fetchAuthenticatedUserProfile(accessToken);
139+
140+
MemberResponseDTO.GitHubProfileDTO dto = MemberResponseDTO.GitHubProfileDTO.builder()
141+
.githubId(profile.getLogin())
142+
.email(profile.getEmail())
143+
.avatarUrl(profile.getAvatarUrl())
144+
.location(profile.getLocation())
145+
.profileUrl(profile.getUrl())
146+
.followersCount(profile.getFollowersCount())
147+
.followingCount(profile.getFollowingCount())
148+
.build();
149+
150+
return ApiResponse.onSuccess(SuccessStatus.MEMBER_GET_GITHUB_PROFILE_OK, dto);
151+
}
152+
153+
154+
127155
}

src/main/java/com/chungang/capstone/openstep/domain/Member/converter/MemberConverter.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,17 @@ public static Member toMember(MemberRequestDTO.MemberSignUpRequestDTO request, S
4141
return member;
4242
}
4343

44+
// public static MemberResponseDTO.GitHubProfileDTO toGitHubProfileDTO(Member member) {
45+
// return MemberResponseDTO.GitHubProfileDTO.builder()
46+
// .githubId(member.getGithubId())
47+
// .email(member.getEmail())
48+
// .avatarUrl(member.getProfileImageUrl())
49+
// .location(member.getLocation())
50+
// .profileUrl(member.getGithubProfileUrl())
51+
// .followersCount(member.getFollowersCount() != null ? member.getFollowersCount() : 0)
52+
// .followingCount(member.getFollowingCount() != null ? member.getFollowingCount() : 0)
53+
// .build();
54+
// }
55+
56+
4457
}

src/main/java/com/chungang/capstone/openstep/domain/Member/dto/MemberResponseDTO.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,17 @@ public record TokenRefreshResponseDTO(
4747
String githubId,
4848
Long memberId) {}
4949

50+
51+
@Builder
52+
public record GitHubProfileDTO(
53+
String githubId,
54+
String email,
55+
String avatarUrl,
56+
String location,
57+
String profileUrl,
58+
int followersCount,
59+
int followingCount
60+
) {}
61+
62+
5063
}

src/main/java/com/chungang/capstone/openstep/domain/Member/entity/Member.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,18 @@ public class Member extends BaseEntity {
4141

4242
private int xp;
4343

44+
// @Column(nullable = true)
45+
// private String location;
46+
//
47+
// @Column(nullable = true)
48+
// private String githubProfileUrl;
49+
//
50+
// @Column(nullable = true)
51+
// private Integer followersCount;
52+
//
53+
// @Column(nullable = true)
54+
// private Integer followingCount;
55+
4456
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true)
4557
private List<MemberLanguage> languages = new ArrayList<>();
4658

src/main/java/com/chungang/capstone/openstep/domain/Member/service/MemberQueryService.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import java.util.List;
44

5+
import com.chungang.capstone.openstep.domain.Member.entity.Member;
6+
import com.chungang.capstone.openstep.domain.Member.repository.MemberRepository;
7+
import com.chungang.capstone.openstep.global.apiPayload.exception.handler.MemberHandler;
58
import org.springframework.stereotype.Service;
69
import org.springframework.transaction.annotation.Transactional;
710

@@ -13,13 +16,16 @@
1316
import lombok.RequiredArgsConstructor;
1417
import lombok.extern.slf4j.Slf4j;
1518

19+
import static com.chungang.capstone.openstep.global.apiPayload.code.status.ErrorStatus.MEMBER_NOT_FOUND;
20+
1621
@Service
1722
@RequiredArgsConstructor
1823
@Slf4j
1924
@Transactional(readOnly = true)
2025
public class MemberQueryService {
2126
private final MemberDomainRepository memberDomainRepository;
2227
private final MemberLanguageRepository memberLanguageRepository;
28+
private final MemberRepository memberRepository;
2329

2430
// public MemberResponseDTO.DomainsRes getDomains(Long memberId) {
2531
// List<String> domains=memberDomainRepository.findDomainsByMemberId(memberId);
@@ -40,4 +46,9 @@ public MemberResponseDTO.LanguagesRes getLanguages(Long memberId) {
4046
return MemberConverter.toLanguageRes(
4147
memberLanguageRepository.findLanguagesByMemberId(memberId));
4248
}
49+
50+
public Member getMemberById(Long memberId) {
51+
return memberRepository.findById(memberId)
52+
.orElseThrow(() -> new MemberHandler(MEMBER_NOT_FOUND));
53+
}
4354
}

src/main/java/com/chungang/capstone/openstep/global/apiPayload/code/status/SuccessStatus.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public enum SuccessStatus implements BaseCode {
2727
MEMBER_LOGIN_OK(HttpStatus.OK, "MEMBER_1006", "유저 로그인에 성공하였습니다."),
2828
MEMBER_LOGOUT_OK(HttpStatus.OK, "MEMBER_1007", "유저 로그아웃에 성공하였습니다."),
2929
MEMBER_UPDATE_ACCESS_TOKEN_OK(HttpStatus.OK, "MEMBER_1008", "유저 액세스 토큰 재발급에 성공하였습니다."),
30+
MEMBER_GET_GITHUB_PROFILE_OK(HttpStatus.OK, "MEMBER_1009", "유저 깃허브 프로필 조회에 성공하였습니다."),
3031

3132
// 레포지토리 관련 응답
3233
REPO_GET_TRENDING_OK(HttpStatus.OK, "REPO_2001", "트렌딩 레포지토리 리스트를 성공적으로 조회하였습니다."),

0 commit comments

Comments
 (0)