Skip to content

Commit

Permalink
Merge branch 'dev' of https://github.com/Stumeet/STUMEET-SERVER into …
Browse files Browse the repository at this point in the history
…STMT-146-fix-file-upload
  • Loading branch information
05AM committed Mar 12, 2024
2 parents ff65af5 + 547f203 commit 9ddf164
Show file tree
Hide file tree
Showing 53 changed files with 893 additions and 75 deletions.
18 changes: 17 additions & 1 deletion src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,15 @@ include::{snippets}/social_login/success/response-fields.adoc[]

.유효한 토큰이 아닌 경우
include::{snippets}/social_login/fail/invalid-token/response-body.adoc[]

.토큰이 전달되지 않은 경우
include::{snippets}/social_login/fail/not-exist-header/response-body.adoc[]

include::{snippets}/social_login/fail/invalid-token/response-fields.adoc[]

.전달한 JWT 토큰의 서명이 유효하지 않은 경우
include::{snippets}/social_login/fail/invalid-signature/response-body.adoc[]
include::{snippets}/social_login/fail/invalid-signature/response-fields.adoc[]

=== 로그아웃

로그아웃 시 사용되는 API입니다.
Expand Down Expand Up @@ -144,6 +148,18 @@ include::{snippets}/validate_nickname/fail/invalid/response-fields.adoc[]
include::{snippets}/validate_nickname/fail/duplicate/response-body.adoc[]
include::{snippets}/validate_nickname/fail/duplicate/response-fields.adoc[]

=== 내 정보 조회

==== GET /api/v1/members/me

===== 요청
include::{snippets}/get-my-profile/success/http-request.adoc[]
include::{snippets}/get-my-profile/success/request-headers.adoc[]

===== 응답 (200)
include::{snippets}/get-my-profile/success/response-body.adoc[]
include::{snippets}/get-my-profile/success/response-fields.adoc[]

=== 사용자 정보 수정

==== PATCH /api/v1/members/me
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.stumeet.server.common.auth.exception;

import com.stumeet.server.common.response.ErrorCode;
import org.springframework.security.core.AuthenticationException;

public class IllegalKeyAlgorithmException extends AuthenticationException {
private final ErrorCode errorCode;

public IllegalKeyAlgorithmException(ErrorCode errorCode, Throwable cause) {
super(errorCode.getMessage(), cause);
this.errorCode = errorCode;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.stumeet.server.common.auth.exception;

import com.stumeet.server.common.response.ErrorCode;
import org.springframework.security.core.AuthenticationException;


public class JwtInvalidSignatureException extends AuthenticationException {

private final ErrorCode errorCode;

public JwtInvalidSignatureException(ErrorCode errorCode, Throwable e) {
super(errorCode.getMessage(), e);
this.errorCode = errorCode;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.stumeet.server.common.auth.exception;

import com.stumeet.server.common.response.ErrorCode;
import org.springframework.security.core.AuthenticationException;

public class JwtTokenParsingException extends AuthenticationException {
private final ErrorCode errorCode;

public JwtTokenParsingException(ErrorCode errorCode, Throwable e) {
super(errorCode.getMessage(), e);
this.errorCode = errorCode;
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package com.stumeet.server.common.client.oauth.apple;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.stumeet.server.common.client.oauth.apple.model.ApplePublicKeyResponses;
import com.stumeet.server.common.auth.exception.IllegalKeyAlgorithmException;
import com.stumeet.server.common.auth.exception.JwtInvalidSignatureException;
import com.stumeet.server.common.auth.exception.JwtTokenParsingException;
import com.stumeet.server.common.response.ErrorCode;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.SignatureException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

Expand Down Expand Up @@ -36,7 +42,7 @@ public PublicKey getSecretKey(ApplePublicKeyResponses publicKeys, String accessT

return keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new RuntimeException(e);
throw new IllegalKeyAlgorithmException(ErrorCode.ILLEGAL_KEY_ALGORITHM_EXCEPTION, e);
}
}

Expand All @@ -45,18 +51,22 @@ private String extractKid(String accessToken) {
String header = new String(Base64.getUrlDecoder().decode(splitToken[0]));
try {
return objectMapper.readTree(header).get("kid").asText();
} catch (Exception e) {
throw new RuntimeException(e);
} catch (JsonProcessingException e ) {
throw new JwtTokenParsingException(ErrorCode.JWT_TOKEN_PARSING_EXCEPTION, e);
}
}


public String extractUserId(PublicKey publicKey, String idToken) {
return Jwts.parser()
.verifyWith(publicKey)
.build()
.parseSignedClaims(idToken)
.getPayload()
.getSubject();
try {
return Jwts.parser()
.verifyWith(publicKey)
.build()
.parseSignedClaims(idToken)
.getPayload()
.getSubject();
} catch (SignatureException e) {
throw new JwtInvalidSignatureException(ErrorCode.JWT_INVALID_SIGNATURE_EXCEPTION, e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public static ApiResponse<List<Error>> fail(ErrorCode errorCode, BindingResult b
}

// TODO: 삭제 예정 메서드 : 해당 메서드를 사용 부분 수정 요망

public static <T> ApiResponse<T> success(int code, String message, T data) {
return new ApiResponse<>(code, message, data);
}
Expand All @@ -48,4 +49,5 @@ public static ApiResponse<Void> success(int code, String message) {
public static ApiResponse<Void> fail(int code, String message) {
return new ApiResponse<>(code, message, null);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ public enum ErrorCode {
*/
ACCESS_DENIED_EXCEPTION(HttpStatus.FORBIDDEN, "유효하지 않은 요청입니다."),

/*
404 - NOT FOUND
*/
STUDY_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 스터디 입니다."),

/*
415 - UNSUPPORTED MEDIA TYPE
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public enum SuccessCode {

GET_SUCCESS(HttpStatus.OK, "조회에 성공했습니다."),
SIGN_UP_SUCCESS(HttpStatus.CREATED, "회원가입에 성공했습니다."),
FILE_UPLOAD_SUCCESS(HttpStatus.CREATED, "파일 업로드에 성공했습니다.")
;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
import com.stumeet.server.common.annotation.WebAdapter;
import com.stumeet.server.common.auth.model.LoginMember;
import com.stumeet.server.common.model.ApiResponse;
import com.stumeet.server.member.adapter.in.web.response.MemberProfileResponse;
import com.stumeet.server.member.application.port.in.MemberProfileUseCase;
import com.stumeet.server.member.application.port.in.command.MemberUpdateCommand;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.RequestMapping;

Expand All @@ -31,4 +33,15 @@ public ResponseEntity<ApiResponse<Void>> updateMyProfile(
HttpStatus.OK
);
}

@GetMapping("/me")
public ResponseEntity<ApiResponse<MemberProfileResponse>> getMyProfile(
@AuthenticationPrincipal LoginMember member
) {
MemberProfileResponse response = memberProfileUseCase.getProfileById(member.getMember().getId());
return new ResponseEntity<>(
ApiResponse.success(HttpStatus.OK.value(), "내 프로필 조회에 성공했습니다.", response),
HttpStatus.OK
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.stumeet.server.member.adapter.in.web.response;

import lombok.Builder;

@Builder
public record MemberProfileResponse(
Long id,
String image,
String nickname,
String region,
String profession,
String tier,
double experience
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.stumeet.server.common.model.BaseTimeEntity;
import com.stumeet.server.member.domain.AuthType;
import com.stumeet.server.member.domain.MemberRank;
import com.stumeet.server.member.domain.MemberTier;
import com.stumeet.server.member.domain.UserRole;
import com.stumeet.server.profession.adapter.out.persistence.ProfessionJpaEntity;
import jakarta.persistence.*;
Expand Down Expand Up @@ -37,10 +37,10 @@ public class MemberJpaEntity extends BaseTimeEntity {
@Comment("멤버 이미지 URL")
private String image;

@Column(name = "rank", length = 50, nullable = false)
@Column(name = "tier", length = 50, nullable = false)
@Enumerated(EnumType.STRING)
@Comment("등급")
private MemberRank rank;
private MemberTier tier;

@Column(name = "experience", nullable = false)
@Comment("경험치")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public MemberJpaEntity toEntity(Member domain) {
.id(domain.getId())
.name(domain.getName())
.image(domain.getImage())
.rank(domain.getLevel().getRank())
.tier(domain.getLevel().getTier())
.experience(domain.getLevel().getExperience())
.region(domain.getRegion())
.profession(professionPersistenceMapper.toEntity(domain.getProfession()))
Expand All @@ -28,7 +28,7 @@ public MemberJpaEntity toEntity(Member domain) {

public Member toDomain(MemberJpaEntity entity) {
MemberLevel level = MemberLevel.builder()
.rank(entity.getRank())
.tier(entity.getTier())
.experience(entity.getExperience())
.build();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.stumeet.server.member.application.port.in;

import com.stumeet.server.member.adapter.in.web.response.MemberProfileResponse;
import com.stumeet.server.member.application.port.in.command.MemberSignupCommand;
import com.stumeet.server.member.application.port.in.command.MemberUpdateCommand;
import com.stumeet.server.member.domain.Member;
Expand All @@ -8,4 +9,6 @@ public interface MemberProfileUseCase {
void signup(Member member, MemberSignupCommand request);

void updateProfile(Member member, MemberUpdateCommand request);

MemberProfileResponse getProfileById(Long id);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.stumeet.server.member.application.port.in.mapper;

import com.stumeet.server.member.adapter.in.web.response.MemberProfileResponse;
import com.stumeet.server.member.domain.Member;
import org.springframework.stereotype.Component;

@Component
public class MemberUseCaseMapper {

public MemberProfileResponse toProfileResponse(Member member) {
return MemberProfileResponse.builder()
.id(member.getId())
.image(member.getImage())
.nickname(member.getName())
.region(member.getRegion())
.profession(member.getProfession().getName())
.tier(member.getLevel().getTier().getName())
.experience(member.getLevel().getExperience())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public Member getMemberOrCreate(OAuthUserProfileResponse response, String provid
member = memberQueryPort.getByOAuthProviderId(response.id(), oAuthProvider);
} else {
MemberLevel initialLevel = MemberLevel.builder()
.rank(MemberRank.SEED)
.tier(MemberTier.SEED)
.experience(0.0)
.build();
member = memberCommandPort.save(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

import com.stumeet.server.common.annotation.UseCase;
import com.stumeet.server.file.application.port.in.FileUploadUseCase;
import com.stumeet.server.member.adapter.in.web.response.MemberProfileResponse;
import com.stumeet.server.member.application.port.in.MemberProfileUseCase;
import com.stumeet.server.member.application.port.in.command.MemberProfileCommand;
import com.stumeet.server.member.application.port.in.command.MemberSignupCommand;
import com.stumeet.server.member.application.port.in.command.MemberUpdateCommand;
import com.stumeet.server.member.application.port.in.mapper.MemberUseCaseMapper;
import com.stumeet.server.member.application.port.out.MemberCommandPort;
import com.stumeet.server.member.application.port.out.MemberQueryPort;
import com.stumeet.server.member.domain.Member;
import com.stumeet.server.profession.application.port.in.ProfessionQueryUseCase;
import com.stumeet.server.profession.domain.Profession;
Expand All @@ -21,6 +24,9 @@ public class MemberProfileService implements MemberProfileUseCase {
private final ProfessionQueryUseCase professionQueryUseCase;
private final FileUploadUseCase fileUploadUseCase;
private final MemberCommandPort memberCommandPort;
private final MemberQueryPort memberQueryPort;
private final MemberUseCaseMapper memberUseCaseMapper;


@Override
public void signup(Member member, MemberSignupCommand request) {
Expand Down Expand Up @@ -61,4 +67,10 @@ public void updateProfile(Member member, MemberUpdateCommand request) {
memberCommandPort.update(member);
}

@Override
public MemberProfileResponse getProfileById(Long id) {
Member member = memberQueryPort.getById(id);
return memberUseCaseMapper.toProfileResponse(member);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
@Builder
public class MemberLevel {

private MemberRank rank;
private MemberTier tier;
private double experience;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

@RequiredArgsConstructor
@Getter
public enum MemberRank {
public enum MemberTier {
SEED("씨앗"),
SPROUT("새싹"),
LEAF("잎"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.stumeet.server.study.adapter.in.web;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import com.stumeet.server.common.annotation.WebAdapter;
import com.stumeet.server.common.model.ApiResponse;
import com.stumeet.server.common.response.SuccessCode;
import com.stumeet.server.study.adapter.in.web.response.StudyDetailResponse;
import com.stumeet.server.study.application.port.in.StudyQueryUseCase;

import lombok.RequiredArgsConstructor;

@WebAdapter
@RequestMapping("/api/v1/studies")
@RequiredArgsConstructor
public class StudyQueryApi {

private final StudyQueryUseCase studyQueryUseCase;

@GetMapping("/{id}")
public ResponseEntity<ApiResponse<StudyDetailResponse>> getStudy(
@PathVariable(name = "id") Long id
) {
StudyDetailResponse response = studyQueryUseCase.getStudyDetailById(id);
return ResponseEntity.ok(ApiResponse.success(SuccessCode.GET_SUCCESS, response));
}
}
Loading

0 comments on commit 9ddf164

Please sign in to comment.