diff --git a/app/src/integration-test/groovy/com/depromeet/threedays/front/domain/usecase/member/SaveMemberConsentUseCaseSpec.groovy b/app/src/integration-test/groovy/com/depromeet/threedays/front/domain/usecase/member/SaveMemberConsentUseCaseSpec.groovy deleted file mode 100644 index 2b0c8bf1..00000000 --- a/app/src/integration-test/groovy/com/depromeet/threedays/front/domain/usecase/member/SaveMemberConsentUseCaseSpec.groovy +++ /dev/null @@ -1,34 +0,0 @@ -package com.depromeet.threedays.front.domain.usecase.member - -import com.depromeet.threedays.data.enums.MemberStatus -import com.depromeet.threedays.front.IntegrationTestSpecification -import com.depromeet.threedays.front.data.member.MemberInitializer -import com.depromeet.threedays.front.persistence.repository.member.MemberRepository -import com.depromeet.threedays.front.web.request.member.MemberNotificationConsentUpdateRequest -import org.springframework.beans.factory.annotation.Autowired - -class SaveMemberConsentUseCaseSpec extends IntegrationTestSpecification { - @Autowired - MemberInitializer initializer - MemberRepository mockRepo - SaveConsentUseCase useCase - - def setup() { - initializer.initialize() - mockRepo = Mock(MemberRepository.class) - useCase = new SaveConsentUseCase(mockRepo) - } - - def "사용자는 알림 수신 정책을 수정할 수 있다"() { - setup: - def id = initializer.data[2] - def expected = MemberNotificationConsentUpdateRequest.builder().notificationConsent(false).build() - - when: - def actual = useCase.execute(expected) - - then: - mockRepo.findByIdAndStatus(_ as Long, _ as MemberStatus) >> Optional.of(id) - !actual.notificationConsent - } -} diff --git a/app/src/integration-test/groovy/com/depromeet/threedays/front/domain/usecase/member/SaveMemberNameUseCaseSpec.groovy b/app/src/integration-test/groovy/com/depromeet/threedays/front/domain/usecase/member/SaveMemberNameUseCaseSpec.groovy deleted file mode 100644 index 24267a69..00000000 --- a/app/src/integration-test/groovy/com/depromeet/threedays/front/domain/usecase/member/SaveMemberNameUseCaseSpec.groovy +++ /dev/null @@ -1,35 +0,0 @@ -package com.depromeet.threedays.front.domain.usecase.member - -import com.depromeet.threedays.data.enums.MemberStatus -import com.depromeet.threedays.front.IntegrationTestSpecification -import com.depromeet.threedays.front.data.member.MemberInitializer -import com.depromeet.threedays.front.persistence.repository.member.MemberRepository -import com.depromeet.threedays.front.web.request.member.MemberNameUpdateRequest -import org.springframework.beans.factory.annotation.Autowired - -class SaveMemberNameUseCaseSpec extends IntegrationTestSpecification { - - @Autowired - MemberInitializer initializer - MemberRepository mockRepo - SaveNameUseCase useCase - - def setup() { - mockRepo = Mock(MemberRepository.class) - useCase = new SaveNameUseCase(mockRepo) - initializer.initialize() - } - - def "사용자는 이름을 새로 등록할 수 있다"() { - setup: - def id = initializer.data[2] - def expected = MemberNameUpdateRequest.builder().name("admin").build() - - when: - def actual = useCase.execute(expected) - - then: - mockRepo.findByIdAndStatus(_ as Long, _ as MemberStatus) >> Optional.of(id) - actual.name == expected.name - } -} diff --git a/app/src/integration-test/groovy/com/depromeet/threedays/front/domain/usecase/member/SaveMemberResourceUseCaseSpec.groovy b/app/src/integration-test/groovy/com/depromeet/threedays/front/domain/usecase/member/UpdateMemberUseCaseSpec.groovy similarity index 66% rename from app/src/integration-test/groovy/com/depromeet/threedays/front/domain/usecase/member/SaveMemberResourceUseCaseSpec.groovy rename to app/src/integration-test/groovy/com/depromeet/threedays/front/domain/usecase/member/UpdateMemberUseCaseSpec.groovy index 2abb40d2..d674ff6a 100644 --- a/app/src/integration-test/groovy/com/depromeet/threedays/front/domain/usecase/member/SaveMemberResourceUseCaseSpec.groovy +++ b/app/src/integration-test/groovy/com/depromeet/threedays/front/domain/usecase/member/UpdateMemberUseCaseSpec.groovy @@ -4,30 +4,31 @@ import com.depromeet.threedays.data.enums.MemberStatus import com.depromeet.threedays.front.IntegrationTestSpecification import com.depromeet.threedays.front.data.member.MemberInitializer import com.depromeet.threedays.front.persistence.repository.member.MemberRepository -import com.depromeet.threedays.front.web.request.member.MemberResourceUpdateRequest +import com.depromeet.threedays.front.web.request.member.MemberUpdateRequest import org.json.simple.JSONObject import org.springframework.beans.factory.annotation.Autowired -class SaveMemberResourceUseCaseSpec extends IntegrationTestSpecification { + +class UpdateMemberUseCaseSpec extends IntegrationTestSpecification { @Autowired MemberInitializer initializer MemberRepository mockRepo - SaveResourceUseCase useCase + UpdateMemberUseCase useCase def setup() { initializer.initialize() mockRepo = Mock(MemberRepository.class) - useCase = new SaveResourceUseCase(mockRepo) + useCase = new UpdateMemberUseCase(mockRepo) } - def "사용자는 resource 값을 추가할 수 있다."() { + def "사용자는 name 데이터를 업데이트 할 수 있다"() { setup: def id = initializer.data[2] def json = new JSONObject() json.put("test", "test") - def expected = MemberResourceUpdateRequest.builder().resource(json).build() + def expected = MemberUpdateRequest.builder().resource(json).name("admin").notificationConsent(false).build() when: def actual = useCase.execute(expected) @@ -35,5 +36,7 @@ class SaveMemberResourceUseCaseSpec extends IntegrationTestSpecification { then: mockRepo.findByIdAndStatus(_ as Long, _ as MemberStatus) >> Optional.of(id) actual.resource.get("test") == json.get("test") + actual.name == expected.name + !actual.notificationConsent } } diff --git a/app/src/main/java/com/depromeet/threedays/front/domain/usecase/member/SaveConsentUseCase.java b/app/src/main/java/com/depromeet/threedays/front/domain/usecase/member/SaveConsentUseCase.java deleted file mode 100644 index 436da914..00000000 --- a/app/src/main/java/com/depromeet/threedays/front/domain/usecase/member/SaveConsentUseCase.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.depromeet.threedays.front.domain.usecase.member; - -import com.depromeet.threedays.data.entity.member.MemberEntity; -import com.depromeet.threedays.data.enums.MemberStatus; -import com.depromeet.threedays.front.config.security.AuditorHolder; -import com.depromeet.threedays.front.domain.converter.member.MemberConverter; -import com.depromeet.threedays.front.domain.model.member.Member; -import com.depromeet.threedays.front.exception.ResourceNotFoundException; -import com.depromeet.threedays.front.persistence.repository.member.MemberRepository; -import com.depromeet.threedays.front.web.request.member.MemberNotificationConsentUpdateRequest; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Transactional -@RequiredArgsConstructor -@Service -public class SaveConsentUseCase { - - private final MemberRepository memberRepository; - - public Member execute(final MemberNotificationConsentUpdateRequest request) { - Long memberId = AuditorHolder.get(); - return MemberConverter.from(updateNotificationConsent(memberId, request)); - } - - public MemberEntity updateNotificationConsent( - final Long memberId, final MemberNotificationConsentUpdateRequest request) { - return memberRepository - .findByIdAndStatus(memberId, MemberStatus.REGULAR) - .map(it -> it.updateNotificationConsent(request.isNotificationConsent())) - .orElseThrow(() -> new ResourceNotFoundException("member.not.found")); - } -} diff --git a/app/src/main/java/com/depromeet/threedays/front/domain/usecase/member/SaveNameUseCase.java b/app/src/main/java/com/depromeet/threedays/front/domain/usecase/member/SaveNameUseCase.java deleted file mode 100644 index f2430128..00000000 --- a/app/src/main/java/com/depromeet/threedays/front/domain/usecase/member/SaveNameUseCase.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.depromeet.threedays.front.domain.usecase.member; - -import com.depromeet.threedays.data.entity.member.MemberEntity; -import com.depromeet.threedays.data.enums.MemberStatus; -import com.depromeet.threedays.front.config.security.AuditorHolder; -import com.depromeet.threedays.front.domain.converter.member.MemberConverter; -import com.depromeet.threedays.front.domain.model.member.Member; -import com.depromeet.threedays.front.exception.ResourceNotFoundException; -import com.depromeet.threedays.front.persistence.repository.member.MemberRepository; -import com.depromeet.threedays.front.web.request.member.MemberNameUpdateRequest; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Transactional -@RequiredArgsConstructor -@Service -public class SaveNameUseCase { - - private final MemberRepository memberRepository; - - public Member execute(final MemberNameUpdateRequest request) { - Long memberId = AuditorHolder.get(); - return MemberConverter.from(this.updateName(memberId, request)); - } - - public MemberEntity updateName(final Long memberId, final MemberNameUpdateRequest request) { - return memberRepository - .findByIdAndStatus(memberId, MemberStatus.REGULAR) - .map(it -> it.updateName(request.getName())) - .orElseThrow(() -> new ResourceNotFoundException("member.not.found")); - } -} diff --git a/app/src/main/java/com/depromeet/threedays/front/domain/usecase/member/SaveResourceUseCase.java b/app/src/main/java/com/depromeet/threedays/front/domain/usecase/member/UpdateMemberUseCase.java similarity index 63% rename from app/src/main/java/com/depromeet/threedays/front/domain/usecase/member/SaveResourceUseCase.java rename to app/src/main/java/com/depromeet/threedays/front/domain/usecase/member/UpdateMemberUseCase.java index 278d897d..05dc4f66 100644 --- a/app/src/main/java/com/depromeet/threedays/front/domain/usecase/member/SaveResourceUseCase.java +++ b/app/src/main/java/com/depromeet/threedays/front/domain/usecase/member/UpdateMemberUseCase.java @@ -8,36 +8,38 @@ import com.depromeet.threedays.front.exception.ResourceNotFoundException; import com.depromeet.threedays.front.persistence.repository.member.MemberRepository; import com.depromeet.threedays.front.support.converter.MemberInfoJsonConverter; -import com.depromeet.threedays.front.web.request.member.MemberResourceUpdateRequest; +import com.depromeet.threedays.front.web.request.member.MemberUpdateRequest; import com.google.gson.JsonElement; import java.util.Map; import java.util.Set; import lombok.RequiredArgsConstructor; import org.json.simple.JSONObject; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -@Transactional @RequiredArgsConstructor @Service -public class SaveResourceUseCase { +public class UpdateMemberUseCase { - private final MemberRepository memberRepository; + private final MemberRepository repository; - public Member execute(final MemberResourceUpdateRequest request) { - Long memberId = AuditorHolder.get(); - return MemberConverter.from(this.updateResource(memberId, request)); + public Member execute(final MemberUpdateRequest request) { + + return MemberConverter.from(this.update(AuditorHolder.get(), request)); } - private MemberEntity updateResource( - final Long memberId, final MemberResourceUpdateRequest request) { - return memberRepository + private MemberEntity update(final Long memberId, final MemberUpdateRequest request) { + return repository .findByIdAndStatus(memberId, MemberStatus.REGULAR) - .map(it -> it.updateResource(this.updateResource(it.getResource(), request.getResource()))) + .map( + it -> + it.update( + request.getName(), + request.isNotificationConsent(), + this.parseSource(it.getResource(), request.getResource()))) .orElseThrow(() -> new ResourceNotFoundException("member.not.found")); } - private String updateResource(String resource, JSONObject newResource) { + private String parseSource(final String resource, final JSONObject newResource) { JSONObject asis = MemberInfoJsonConverter.from(resource); Set> element = newResource.entrySet(); diff --git a/app/src/main/java/com/depromeet/threedays/front/web/controller/MemberController.java b/app/src/main/java/com/depromeet/threedays/front/web/controller/MemberController.java index eaa3fe55..03b47fe1 100644 --- a/app/src/main/java/com/depromeet/threedays/front/web/controller/MemberController.java +++ b/app/src/main/java/com/depromeet/threedays/front/web/controller/MemberController.java @@ -12,10 +12,7 @@ import com.depromeet.threedays.front.support.ApiResponseGenerator; import com.depromeet.threedays.front.support.MessageCode; import com.depromeet.threedays.front.web.request.client.DeleteClientRequest; -import com.depromeet.threedays.front.web.request.member.MemberNameUpdateRequest; -import com.depromeet.threedays.front.web.request.member.MemberNotificationConsentUpdateRequest; -import com.depromeet.threedays.front.web.request.member.MemberResourceUpdateRequest; -import com.depromeet.threedays.front.web.request.member.SignMemberRequest; +import com.depromeet.threedays.front.web.request.member.*; import com.depromeet.threedays.front.web.response.SaveMemberResponse; import javax.validation.Valid; import lombok.RequiredArgsConstructor; @@ -29,9 +26,7 @@ public class MemberController { private final SignMemberUseCaseFacade signUseCase; - private final SaveNameUseCase saveNameUseCase; - private final SaveConsentUseCase saveConsentUseCase; - private final SaveResourceUseCase saveResourceUseCase; + private final UpdateMemberUseCase updateMemberUsecase; private final GetTokenUseCase getTokenUseCase; private final DeleteMemberUseCase deleteUseCase; private final DeleteClientUseCase deleteClientUseCase; @@ -50,25 +45,10 @@ public ApiResponse> add( } } - /** 닉네임 변경 */ - @PatchMapping("/name") - public ApiResponse> updateName( - @RequestBody @Valid MemberNameUpdateRequest request) { - return ApiResponseGenerator.success(saveNameUseCase.execute(request), HttpStatus.OK); - } - - /** 알림 수신 설정 변경 */ - @PatchMapping("/consents") - public ApiResponse> updateConsent( - @RequestBody @Valid MemberNotificationConsentUpdateRequest request) { - return ApiResponseGenerator.success(saveConsentUseCase.execute(request), HttpStatus.OK); - } - - /** 사용자 정보 업데이트 */ - @PatchMapping("/resources") - public ApiResponse> updateResource( - @RequestBody @Valid MemberResourceUpdateRequest request) { - return ApiResponseGenerator.success(saveResourceUseCase.execute(request), HttpStatus.OK); + @PatchMapping("/me") + public ApiResponse> updateMember( + @RequestBody @Valid MemberUpdateRequest request) { + return ApiResponseGenerator.success(updateMemberUsecase.execute(request), HttpStatus.OK); } /** 토근 발급 */ diff --git a/app/src/main/java/com/depromeet/threedays/front/web/request/member/MemberUpdateRequest.java b/app/src/main/java/com/depromeet/threedays/front/web/request/member/MemberUpdateRequest.java new file mode 100644 index 00000000..de2e5fc4 --- /dev/null +++ b/app/src/main/java/com/depromeet/threedays/front/web/request/member/MemberUpdateRequest.java @@ -0,0 +1,18 @@ +package com.depromeet.threedays.front.web.request.member; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.json.simple.JSONObject; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder(toBuilder = true) +public class MemberUpdateRequest { + + private String name; + private boolean notificationConsent; + private JSONObject resource; +} diff --git a/app/src/main/resources/static/openapi3.yml b/app/src/main/resources/static/openapi3.yml index 09b66f2b..45150475 100644 --- a/app/src/main/resources/static/openapi3.yml +++ b/app/src/main/resources/static/openapi3.yml @@ -431,87 +431,17 @@ paths: example: - code: "uncaught error" message: "서버 에러" - /api/v1/members/logout: - post: - tags: - - Member - summary: 로그아웃 API - operationId: logoutMember - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - identificationKey: - type: string - example: identificationKey - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/ApiResponse' - '500': - description: INTERNAL SERVER ERROR - content: - application/json: - schema: - $ref: '#/components/schemas/ApiFailureResponse' - example: - - code: "uncaught error" - message: "서버 에러" - /api/v1/members/name: - patch: - tags: - - Member - summary: 닉네임 수정 API - operationId: patchMemberName - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/MemberNameUpdateRequest' - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/Member' - '400': - description: BAD REQUEST - content: - application/json: - schema: - $ref: '#/components/schemas/ApiFailureResponse' - example: - - code: "invalid.id" - message: "id는 숫자여야만 합니다." - '500': - description: INTERNAL SERVER ERROR - content: - application/json: - schema: - $ref: '#/components/schemas/ApiFailureResponse' - example: - - code: "uncaught error" - message: "서버 에러" - /api/v1/members/consents: patch: tags: - Member - summary: 알림수신여부 수정 API - operationId: patchMemberNotificationConsent + summary: 사용자 관련 정보 수정 API + operationId: patchMember requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/MemberNotificationConsentUpdateRequest' + $ref: '#/components/schemas/MemberUpdateRequest' responses: '200': description: OK @@ -537,35 +467,29 @@ paths: example: - code: "uncaught error" message: "서버 에러" - /api/v1/members/resource: - patch: + /api/v1/members/logout: + post: tags: - Member - summary: 리소스 수정 API - description: 멤버용 리소스를 저장하기 위한 API입니다. 전송한 키값이 기존에 존재하는 경우 새로운 값으로 덮어써지며, 새로운 키값의 경우에는 추가됩니다(리소스 오브젝트가 전체 교체되지 않습니다) - operationId: patchMemberResource + summary: 로그아웃 API + operationId: logoutMember requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/MemberResourceRequest' + type: object + properties: + identificationKey: + type: string + example: identificationKey responses: '200': description: OK content: application/json: schema: - $ref: '#/components/schemas/Member' - '400': - description: BAD REQUEST - content: - application/json: - schema: - $ref: '#/components/schemas/ApiFailureResponse' - example: - - code: "invalid.id" - message: "id는 숫자여야만 합니다." + $ref: '#/components/schemas/ApiResponse' '500': description: INTERNAL SERVER ERROR content: @@ -1053,6 +977,17 @@ components: socialToken: type: string example: "oauth access token" + MemberUpdateRequest: + type: object + properties: + name: + type: string + example: admin + notificationConsent: + type: boolean + example: false + resource: + type: object MemberNameUpdateRequest: type: object properties: diff --git a/data/src/main/java/com/depromeet/threedays/data/entity/member/MemberEntity.java b/data/src/main/java/com/depromeet/threedays/data/entity/member/MemberEntity.java index 23925dd3..0a355aff 100644 --- a/data/src/main/java/com/depromeet/threedays/data/entity/member/MemberEntity.java +++ b/data/src/main/java/com/depromeet/threedays/data/entity/member/MemberEntity.java @@ -69,6 +69,13 @@ public MemberEntity updateResource(String resource) { return this; } + public MemberEntity update(String name, Boolean consent, String resource) { + this.name = name; + this.notificationConsent = consent; + this.resource = resource; + return this; + } + public MemberEntity withdraw() { status = MemberStatus.WITHDRAWN; return this;