Skip to content

Commit

Permalink
Merge pull request #73 from Stumeet/dev
Browse files Browse the repository at this point in the history
✨ [STMT-90] 내 정보 수정 API 구현 (#72)
  • Loading branch information
zxcv9203 authored Mar 4, 2024
2 parents 0a44f4c + fa104a2 commit ff4a379
Show file tree
Hide file tree
Showing 30 changed files with 597 additions and 52 deletions.
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

0 comments on commit ff4a379

Please sign in to comment.