diff --git a/src/docs/asciidoc/group/group-deletion.adoc b/src/docs/asciidoc/group/group-deletion.adoc new file mode 100644 index 00000000..1a43672c --- /dev/null +++ b/src/docs/asciidoc/group/group-deletion.adoc @@ -0,0 +1,7 @@ +== 그룹 삭제 + +=== `POST /api/groups/{groupId}` + +=== `204 No Content` + +operation::group_acceptance_test/delete_group[snippets='path-parameters,http-request,http-response'] diff --git a/src/docs/asciidoc/group/group-name-validation.adoc b/src/docs/asciidoc/group/group-name-validation.adoc new file mode 100644 index 00000000..26cbadee --- /dev/null +++ b/src/docs/asciidoc/group/group-name-validation.adoc @@ -0,0 +1,7 @@ +== 그룹 이름 중복 확인 + +=== `GET /groups/{groupId}` + +=== `200 Ok` + +operation::group_acceptance_test/validate_group_name[snippets='request-parameters,response-fields,http-request,http-response'] diff --git a/src/docs/asciidoc/group/group-read-one.adoc b/src/docs/asciidoc/group/group-read-one.adoc new file mode 100644 index 00000000..e91db70f --- /dev/null +++ b/src/docs/asciidoc/group/group-read-one.adoc @@ -0,0 +1,7 @@ +== 그룹 상세 조회 + +=== `GET /groups/{groupId}` + +=== `200 Ok` + +operation::group_acceptance_test/read_one[snippets='path-parameters,response-fields,http-request,http-response'] diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc index d694fe6a..9af07cf5 100644 --- a/src/docs/asciidoc/index.adoc +++ b/src/docs/asciidoc/index.adoc @@ -26,12 +26,21 @@ include::user/email-validation.adoc[] // 닉네임 중복확인 include::user/nickname-validation.adoc[] +// 그룹 상세 조회 +include::group/group-read-one.adoc[] + // 그룹 생성 include::group/group-creation.adoc[] // 그룹 수정 include::group/group-modification.adoc[] +// 그룹 삭제 +include::group/group-deletion.adoc[] + +// 그룹 이름 중복확인 +include::group/group-name-validation.adoc[] + // 스케줄 후보 전체 조회 include::schedule/schedule-candidate-read-all.adoc[] diff --git a/src/main/java/com/postsquad/scoup/web/group/controller/GroupController.java b/src/main/java/com/postsquad/scoup/web/group/controller/GroupController.java index a56353a9..d927d9b3 100644 --- a/src/main/java/com/postsquad/scoup/web/group/controller/GroupController.java +++ b/src/main/java/com/postsquad/scoup/web/group/controller/GroupController.java @@ -4,6 +4,9 @@ import com.postsquad.scoup.web.error.controller.response.ErrorResponse; import com.postsquad.scoup.web.group.controller.request.GroupCreationRequest; import com.postsquad.scoup.web.group.controller.request.GroupModificationRequest; +import com.postsquad.scoup.web.group.controller.request.GroupValidationRequest; +import com.postsquad.scoup.web.group.controller.response.GroupReadOneResponse; +import com.postsquad.scoup.web.group.controller.response.GroupValidationResponse; import com.postsquad.scoup.web.group.exception.GroupCreationFailedException; import com.postsquad.scoup.web.group.service.GroupService; import com.postsquad.scoup.web.user.LoggedInUser; @@ -21,6 +24,18 @@ public class GroupController { private final GroupService groupService; + @GetMapping("/{groupId}") + public GroupReadOneResponse readOne(@PathVariable long groupId) { + // TODO: GroupService.readOne + return GroupReadOneResponse.builder() + .id(1L) + .image("image") + .name("name") + .description("description") + .build(); + } + + @PostMapping @ResponseStatus(HttpStatus.CREATED) public DefaultPostResponse create(@RequestBody @Valid GroupCreationRequest groupCreationRequest, @LoggedInUser User user) { @@ -38,4 +53,17 @@ public ErrorResponse groupCreationFailedExceptionHandler(GroupCreationFailedExce public void update(@PathVariable Long groupId, @RequestBody @Valid GroupModificationRequest groupModificationRequest, @LoggedInUser User user) { groupService.update(groupId, groupModificationRequest, user); } + + @DeleteMapping("/{groupId}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void delete(@PathVariable Long groupId) { + // TODO: 삭제 서비스, 사용자 인증 + } + + @GetMapping("/validate/group-name") + public GroupValidationResponse validateGroupName(@Valid GroupValidationRequest groupValidationRequest) { + return GroupValidationResponse.builder() + .isExistingName(true) + .build(); + } } diff --git a/src/main/java/com/postsquad/scoup/web/group/controller/request/GroupValidationRequest.java b/src/main/java/com/postsquad/scoup/web/group/controller/request/GroupValidationRequest.java new file mode 100644 index 00000000..d9ade64d --- /dev/null +++ b/src/main/java/com/postsquad/scoup/web/group/controller/request/GroupValidationRequest.java @@ -0,0 +1,18 @@ +package com.postsquad.scoup.web.group.controller.request; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; + +@NoArgsConstructor +@AllArgsConstructor +@Data +@Builder +public class GroupValidationRequest { + + @NotNull + private String groupName; +} diff --git a/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupDetailResponse.java b/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupDetailResponse.java deleted file mode 100644 index 4245f8fd..00000000 --- a/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupDetailResponse.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.postsquad.scoup.web.group.controller.response; - -import com.postsquad.scoup.web.schedule.controller.response.ScheduleResponses; -import lombok.Getter; - -@Getter -public class GroupDetailResponse extends GroupBaseResponse { - - private GroupMemberResponses members; - - private ScheduleResponses schedules; -} diff --git a/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupReadOneResponse.java b/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupReadOneResponse.java new file mode 100644 index 00000000..af11a510 --- /dev/null +++ b/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupReadOneResponse.java @@ -0,0 +1,22 @@ +package com.postsquad.scoup.web.group.controller.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Data +// TODO: 전체조회 머지되면 BaseResponse와 합치기 +public class GroupReadOneResponse { + + private long id; + + private String name; + + private String description; + + private String image; +} diff --git a/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupValidationResponse.java b/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupValidationResponse.java index eeaa7035..fe310201 100644 --- a/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupValidationResponse.java +++ b/src/main/java/com/postsquad/scoup/web/group/controller/response/GroupValidationResponse.java @@ -1,10 +1,11 @@ package com.postsquad.scoup.web.group.controller.response; -import lombok.Builder; -import lombok.Getter; +import lombok.*; +@NoArgsConstructor +@AllArgsConstructor +@Data @Builder -@Getter public class GroupValidationResponse { @Builder.Default diff --git a/src/test/java/com/postsquad/scoup/web/group/GroupAcceptanceTest.java b/src/test/java/com/postsquad/scoup/web/group/GroupAcceptanceTest.java index 0fcbc284..3e102fb8 100644 --- a/src/test/java/com/postsquad/scoup/web/group/GroupAcceptanceTest.java +++ b/src/test/java/com/postsquad/scoup/web/group/GroupAcceptanceTest.java @@ -6,17 +6,17 @@ import com.postsquad.scoup.web.error.controller.response.ErrorResponse; import com.postsquad.scoup.web.group.controller.request.GroupCreationRequest; import com.postsquad.scoup.web.group.controller.request.GroupModificationRequest; +import com.postsquad.scoup.web.group.controller.response.GroupReadOneResponse; +import com.postsquad.scoup.web.group.controller.response.GroupValidationResponse; import com.postsquad.scoup.web.group.domain.Group; -import com.postsquad.scoup.web.group.provider.CreateGroupProvider; -import com.postsquad.scoup.web.group.provider.CreateGroupWithExistingNameProvider; -import com.postsquad.scoup.web.group.provider.ModifyGroupProvider; -import com.postsquad.scoup.web.group.provider.ValidateGroupCreationRequestProvider; +import com.postsquad.scoup.web.group.provider.*; import io.restassured.RestAssured; import io.restassured.http.ContentType; import io.restassured.response.Response; import io.restassured.specification.RequestSpecification; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ArgumentsSource; import org.springframework.beans.factory.annotation.Autowired; @@ -27,13 +27,32 @@ import java.util.ArrayList; import static org.assertj.core.api.BDDAssertions.then; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.request.RequestDocumentation.*; import static org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.document; public class GroupAcceptanceTest extends AcceptanceTestBase { + private static final Snippet GROUP_READ_ONE_PATH_PARAMETERS = pathParameters( + parameterWithName("groupId") + .description("그룹 ID") + ); + + private static final Snippet GROUP_READ_ONE_RESPONSE_FIELDS = responseFields( + fieldWithPath("id") + .type(JsonFieldType.NUMBER) + .description("id"), + fieldWithPath("name") + .type(JsonFieldType.STRING) + .description("이름"), + fieldWithPath("description") + .type(JsonFieldType.STRING) + .description("설명"), + fieldWithPath("image") + .type(JsonFieldType.STRING) + .description("이미지") + ); + private static final Snippet GROUP_CREATION_REQUEST_FIELDS = requestFields( fieldWithPathAndConstraints("name", GroupCreationRequest.class) .type(JsonFieldType.STRING) @@ -59,6 +78,22 @@ public class GroupAcceptanceTest extends AcceptanceTestBase { .optional() ); + private static final Snippet GROUP_DELETION_PATH_PARAMETERS = pathParameters( + parameterWithName("groupId") + .description("그룹 ID") + ); + + private static final Snippet GROUP_NAME_VALIDATION_REQUEST_FIELDS = requestParameters( + parameterWithName("group_name") + .description("중복 체크할 그룹 명") + ); + + private static final Snippet GROUP_NAME_VALIDATION_RESPONSE_FIELDS = responseFields( + fieldWithPath("is_existing_name") + .type(JsonFieldType.BOOLEAN) + .description("그룹 명 중복 여부") + ); + @Autowired TestEntityManager testEntityManager; @@ -67,6 +102,53 @@ void setUp() { testEntityManager.persist(testUser); } + @Test + @DisplayName("선택한 그룹 정보를 조회할 수 있다.(이미 지나간 일정은 보이면 안 됨)") + void readOne() { + // given + Group givenGroup = Group.builder() + .name("name") + .description("description") + // TODO: .image("image") + .build(); + testEntityManager.persist(givenGroup); + GroupReadOneResponse expectedGroupReadOneResponse = GroupReadOneResponse.builder() + .id(1L) + .image("image") + .name("name") + .description("description") + .build(); + String path = "/groups/{groupId}"; + RequestSpecification givenRequest = RestAssured.given(this.spec) + .baseUri(BASE_URL) + .port(port) + .basePath("/api") + .contentType(ContentType.JSON) + .header("Accept-Language", "en-US") + .header("Authorization", TEST_TOKEN) + .pathParam("groupId", givenGroup.getId()); + + //when + Response actualResponse = givenRequest.when() + .filter(document( + DEFAULT_RESTDOCS_PATH, + GROUP_READ_ONE_PATH_PARAMETERS, + GROUP_READ_ONE_RESPONSE_FIELDS + )) + .log().all() + .get(path); + + //then + actualResponse.then() + .log().all() + .statusCode(HttpStatus.OK.value()); + then(actualResponse.as(GroupReadOneResponse.class)) + // TODO: Add description + .usingRecursiveComparison() + .ignoringFields(ignoringFieldsForResponseWithId) + .isEqualTo(expectedGroupReadOneResponse); + } + @ParameterizedTest @ArgumentsSource(CreateGroupProvider.class) @DisplayName("사용자가 새로운 그룹을 생성할 수 있다") @@ -220,4 +302,80 @@ void modifyGroup(String description, Long givenGroupId, GroupModificationRequest .isEqualTo(expectedGroup) ); } + + @ParameterizedTest + @ArgumentsSource(DeleteGroupProvider.class) + @DisplayName("사용자가 그룹을 삭제 할 수 있다") + void deleteGroup(String description, Group givenGroup) { + testEntityManager.persist(givenGroup); + + String path = "/groups/{groupId}"; + RequestSpecification givenRequest = RestAssured.given(this.spec) + .baseUri(BASE_URL) + .port(port) + .basePath("/api") + .pathParam("groupId", givenGroup.getId()) + .contentType(ContentType.JSON) + .header("Accept-Language", "en-US") + .header("Authorization", TEST_TOKEN); + + // when + Response actualResponse = givenRequest.when() + .filter(document( + DEFAULT_RESTDOCS_PATH, + GROUP_DELETION_PATH_PARAMETERS + )) + .log().all() + .delete(path); + + // then + actualResponse.then() + .log().all() + .statusCode(HttpStatus.NO_CONTENT.value()); + // TODO: DB에 해당 그룹 존재하지 않는 것 확인 + } + + @Test + @DisplayName("이미 가입된 그룹 이름을 입력할 경우 그룹 이름이 중복되었다는 메시지가 반환된다.") + void validateGroupName() { + // given + Group givenGroup = Group.builder() + .name("name") + .description("description") + // TODO: .image("image") + .build(); + testEntityManager.persist(givenGroup); + GroupValidationResponse expectedGroupValidationResponse = GroupValidationResponse.builder() + .isExistingName(true) + .build(); + String path = "/groups/validate/group-name"; + RequestSpecification givenRequest = RestAssured.given(this.spec) + .baseUri(BASE_URL) + .port(port) + .basePath("/api") + .contentType(ContentType.JSON) + .header("Accept-Language", "en-US") + .header("Authorization", TEST_TOKEN) + .queryParam("group_name", givenGroup.getName()); + + //when + Response actualResponse = givenRequest.when() + .filter(document( + DEFAULT_RESTDOCS_PATH, + GROUP_NAME_VALIDATION_REQUEST_FIELDS, + GROUP_NAME_VALIDATION_RESPONSE_FIELDS + )) + .log().all() + .get(path); + + //then + actualResponse.then() + .log().all() + .statusCode(HttpStatus.OK.value()); + then(actualResponse.as(GroupValidationResponse.class)) + // TODO: Add description + .usingRecursiveComparison() + .ignoringFields(ignoringFieldsForResponseWithId) + .isEqualTo(expectedGroupValidationResponse); + } } diff --git a/src/test/java/com/postsquad/scoup/web/group/provider/DeleteGroupProvider.java b/src/test/java/com/postsquad/scoup/web/group/provider/DeleteGroupProvider.java new file mode 100644 index 00000000..084e6a9d --- /dev/null +++ b/src/test/java/com/postsquad/scoup/web/group/provider/DeleteGroupProvider.java @@ -0,0 +1,24 @@ +package com.postsquad.scoup.web.group.provider; + +import com.postsquad.scoup.web.group.domain.Group; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; + +import java.util.stream.Stream; + +public class DeleteGroupProvider implements ArgumentsProvider { + + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + return Stream.of( + Arguments.of( + "성공", + Group.builder() + .name("groupToBeDeleted") + .description("description") + .build() + ) + ); + } +}