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

✨ [STMT-90] 내 정보 수정 API 구현 (#72) #73

Merged
merged 1 commit into from
Mar 4, 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
18 changes: 18 additions & 0 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,24 @@ 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[]

=== 사용자 정보 수정

==== PATCH /api/v1/members/me

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

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

===== 응답 실패 (400)
include::{snippets}/update-my-profile/fail/invalid-request/response-body.adoc[]
include::{snippets}/update-my-profile/fail/invalid-request/response-fields.adoc[]

== 분야 관리
=== 분야 정보 전체 조회

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.stumeet.server.common.annotation.validator;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;

import java.lang.annotation.*;

@Documented
@Constraint(validatedBy = NullOrImageFileValidator.class)
@Target({ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface NullOrImageFile {
String message() default "값이 전달되지 않거나 이미지 파일이어야 합니다.";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.stumeet.server.common.annotation.validator;

import com.stumeet.server.common.util.FileUtil;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.springframework.web.multipart.MultipartFile;

public class NullOrImageFileValidator implements ConstraintValidator<NullOrImageFile, MultipartFile> {
@Override
public boolean isValid(MultipartFile value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}

return FileUtil.isImageFile(value.getOriginalFilename());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.stumeet.server.common.annotation.validator;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;

import java.lang.annotation.*;

@Documented
@Constraint(validatedBy = NullOrNotBlankValidator.class)
@Target({ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface NullOrNotBlank {
String message() default "값이 전달되지 않거나 빈 문자열이 아니어야 합니다.";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.stumeet.server.common.annotation.validator;

import io.micrometer.common.util.StringUtils;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class NullOrNotBlankValidator implements ConstraintValidator<NullOrNotBlank, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}

return StringUtils.isNotBlank(value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.stumeet.server.common.annotation.validator;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;

import java.lang.annotation.*;

@Documented
@Constraint(validatedBy = NullOrPositiveValidator.class)
@Target({ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface NullOrPositive {
String message() default "값이 전달되지 않거나 양수여야 합니다.";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.stumeet.server.common.annotation.validator;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class NullOrPositiveValidator implements ConstraintValidator<NullOrPositive, Long> {
@Override
public boolean isValid(Long value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}

return value > 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.stumeet.server.common.annotation.validator;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;

import java.lang.annotation.*;


@Documented
@Constraint(validatedBy = NullOrValidSizeValidator.class)
@Target({ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface NullOrValidSize {
String message() default "값이 전달되지 않거나 유효한 크기여야 합니다.";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};

int min() default 0;
int max() default 255;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.stumeet.server.common.annotation.validator;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class NullOrValidSizeValidator implements ConstraintValidator<NullOrValidSize, String> {
private int min;
private int max;

@Override
public void initialize(NullOrValidSize constraintAnnotation) {
min = constraintAnnotation.min();
max = constraintAnnotation.max();
}

@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
return value.length() >= min && value.length() <= max;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.stumeet.server.common.auth.service.JwtAuthenticationService;
import com.stumeet.server.common.auth.service.OAuthAuthenticationProvider;
import com.stumeet.server.common.token.JwtTokenProvider;
import com.stumeet.server.member.domain.UserRole;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand Down Expand Up @@ -69,8 +70,9 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
auth.requestMatchers(HttpMethod.POST, "/api/v1/tokens").permitAll();
auth.requestMatchers("/h2-console/**").permitAll();
auth.requestMatchers("/docs/**").permitAll();
auth.requestMatchers("/api/v1/signup").hasAnyAuthority("FIRST_LOGIN");
auth.anyRequest().authenticated();
auth.requestMatchers("/api/v1/signup").hasAnyAuthority(UserRole.FIRST_LOGIN.toString());
auth.requestMatchers("/api/v1/professions").hasAnyAuthority(UserRole.FIRST_LOGIN.toString(), UserRole.MEMBER.toString());
auth.anyRequest().hasAnyAuthority(UserRole.MEMBER.toString());
});

http.securityContext(securityContext -> securityContext.securityContextRepository(securityContextRepository()));
Expand Down
20 changes: 15 additions & 5 deletions src/main/java/com/stumeet/server/common/util/FileUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,31 @@ public static String getContentType(String fileName) {
throw new BusinessException(ErrorCode.INVALID_IMAGE_EXCEPTION);
}

String contentType = fileName
.substring(fileName.lastIndexOf(".") + 1)
.toLowerCase(Locale.ROOT);
String contentType = extractContentType(fileName);

if (!VALID_CONTENT_TYPES.contains(contentType)) {
if (!VALID_CONTENT_TYPES.contains(contentType)) {
throw new BusinessException(ErrorCode.INVALID_FILE_EXTENSION_EXCEPTION);
}

return contentType;
}

public static String createFileName(String directoryPath, String fileName) {
private static String extractContentType(String fileName) {
return fileName
.substring(fileName.lastIndexOf(".") + 1)
.toLowerCase(Locale.ROOT);
}

public static String createFileName(String directoryPath, String fileName) {
String dateTime = LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));

return String.format("%s/%s%s-%s", directoryPath, dateTime, UUID.randomUUID(), fileName);
}

public static boolean isImageFile(String fileName) {
String contentType = extractContentType(fileName);

return VALID_CONTENT_TYPES.contains(contentType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
import com.stumeet.server.common.auth.model.LoginMember;
import com.stumeet.server.common.model.ApiResponse;
import com.stumeet.server.member.adapter.in.web.response.TokenResponse;
import com.stumeet.server.member.application.port.in.MemberAuthUseCase;
import com.stumeet.server.member.application.port.in.MemberSignupCommand;
import com.stumeet.server.member.application.port.in.TokenRenewCommand;
import com.stumeet.server.member.application.port.in.MemberProfileUseCase;
import com.stumeet.server.member.application.port.in.MemberTokenUseCase;
import com.stumeet.server.member.application.port.in.command.MemberSignupCommand;
import com.stumeet.server.member.application.port.in.command.TokenRenewCommand;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
Expand All @@ -21,14 +22,15 @@
@RequiredArgsConstructor
public class MemberAuthApi {

private final MemberAuthUseCase memberAuthUseCase;
private final MemberTokenUseCase memberTokenUseCase;
private final MemberProfileUseCase memberProfileUseCase;

@PostMapping("/signup")
public ResponseEntity<ApiResponse<Void>> signup(
@AuthenticationPrincipal LoginMember loginMember,
@Valid MemberSignupCommand request
) {
memberAuthUseCase.signup(loginMember.getMember(), request);
memberProfileUseCase.signup(loginMember.getMember(), request);

return new ResponseEntity<>(
ApiResponse.success(HttpStatus.OK.value(), "회원가입에 성공했습니다."),
Expand All @@ -40,7 +42,7 @@ public ResponseEntity<ApiResponse<Void>> signup(
public ResponseEntity<ApiResponse<TokenResponse>> renewAccessToken(
@RequestBody @Valid TokenRenewCommand request
) {
TokenResponse response = memberAuthUseCase.renewAccessToken(request);
TokenResponse response = memberTokenUseCase.renewAccessToken(request);

return new ResponseEntity<>(
ApiResponse.success(HttpStatus.OK.value(), "액세스 토큰 재발급에 성공했습니다.", response),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.stumeet.server.member.adapter.in.web;

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.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.PatchMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@WebAdapter
@RequestMapping("/api/v1/members")
@RequiredArgsConstructor
public class MemberProfileApi {

private final MemberProfileUseCase memberProfileUseCase;

@PatchMapping("/me")
public ResponseEntity<ApiResponse<Void>> updateMyProfile(
@AuthenticationPrincipal LoginMember member,
@Valid MemberUpdateCommand request
) {
memberProfileUseCase.updateProfile(member.getMember(), request);
return new ResponseEntity<>(
ApiResponse.success(HttpStatus.OK.value(), "내 프로필 수정에 성공했습니다."),
HttpStatus.OK
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ public Member save(Member member) {
return memberMapper.toDomain(entity);
}

@Override
public void update(Member member) {
save(member);
}

@Override
public Member getByOAuthProviderId(String oAuthProviderId, OAuthProvider provider) {
return memberMapper.toDomain(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.stumeet.server.member.application.port.in;

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;

public interface MemberProfileUseCase {
void signup(Member member, MemberSignupCommand request);

void updateProfile(Member member, MemberUpdateCommand request);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.stumeet.server.member.application.port.in;

import com.stumeet.server.member.adapter.in.web.response.TokenResponse;
import com.stumeet.server.member.domain.Member;
import com.stumeet.server.member.application.port.in.command.TokenRenewCommand;

public interface MemberTokenUseCase {

public interface MemberAuthUseCase {

void signup(Member member, MemberSignupCommand request);

TokenResponse renewAccessToken(TokenRenewCommand request);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.stumeet.server.member.application.port.in.command;

import com.stumeet.server.profession.domain.Profession;
import lombok.Builder;


@Builder
public record MemberProfileCommand(
Profession profession,
String url,
String nickname,
String region
) {
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.stumeet.server.member.application.port.in;
package com.stumeet.server.member.application.port.in.command;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.Size;
import org.springframework.web.multipart.MultipartFile;

Expand All @@ -16,7 +17,7 @@ public record MemberSignupCommand(
@NotBlank(message = "지역을 입력해주세요")
String region,

@NotNull(message = "분야를 선택해주세요")
@Positive(message = "분야를 선택해주세요")
Long profession
) {
}
Loading
Loading