Skip to content

Commit

Permalink
[FEAT] 멤버 관련 API 구현 (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
leegwichan authored Dec 27, 2024
1 parent 5a2ce8b commit 09287ab
Show file tree
Hide file tree
Showing 23 changed files with 378 additions and 18 deletions.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.debatetimer.controller.member;

import com.debatetimer.dto.member.MemberCreateRequest;
import com.debatetimer.dto.member.MemberCreateResponse;
import com.debatetimer.dto.member.TableResponses;
import com.debatetimer.service.member.MemberService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class MemberController implements MemberControllerSwagger{

private final MemberService memberService;

@Override
@GetMapping("/api/table")
public TableResponses getTables(@RequestParam Long memberId) {
return memberService.getTables(memberId);
}

@Override
@PostMapping("/api/member")
@ResponseStatus(HttpStatus.CREATED)
public MemberCreateResponse createMember(@RequestBody MemberCreateRequest request) {
return memberService.createMember(request);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.debatetimer.controller.member;

import com.debatetimer.dto.member.MemberCreateRequest;
import com.debatetimer.dto.member.MemberCreateResponse;
import com.debatetimer.dto.member.TableResponses;
import com.debatetimer.swagger.annotation.ErrorCode400;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;

@Tag(name = "Member API")
public interface MemberControllerSwagger {

@Operation(
summary = "멤버의 토론 시간표 조회",
responses = {
@ApiResponse(
responseCode = "200",
description = "멤버의 토론 시간표 조회 성공",
content = @Content(schema = @Schema(implementation = TableResponses.class))
)
}
)
@ErrorCode400
TableResponses getTables(Long memberId);

@Operation(
summary = "멤버 생성",
requestBody = @RequestBody(
content = @Content(schema = @Schema(implementation = MemberCreateRequest.class))
),
responses = {
@ApiResponse(
responseCode = "201",
description = "멤버 생성 성공",
content = @Content(schema = @Schema(implementation = MemberCreateResponse.class))
)
}
)
@ErrorCode400
MemberCreateResponse createMember(MemberCreateRequest request);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.debatetimer.domain;
package com.debatetimer.domain.member;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.debatetimer.domain.parliamentary_debate;
package com.debatetimer.domain.parliamentary;

import com.debatetimer.domain.Member;
import com.debatetimer.domain.member.Member;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
Expand All @@ -18,7 +18,7 @@
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ParliamentaryTable {

private static final String NAME_REGEX = "^[a-zA-Z가-힣]+$";
private static final String NAME_REGEX = "^[a-zA-Z가-힣 ]+$";
public static final int NAME_MAX_LENGTH = 20;

@Id
Expand Down Expand Up @@ -48,7 +48,7 @@ public ParliamentaryTable(Member member, String name, String agenda, int duratio
}

private void validate(String name, int duration) {
if (name.isEmpty() || name.length() > NAME_MAX_LENGTH) {
if (name.isBlank() || name.length() > NAME_MAX_LENGTH) {
throw new IllegalArgumentException("테이블 이름은 1자 이상 %d자 이하여야 합니다".formatted(NAME_MAX_LENGTH));
}
if (!name.matches(NAME_REGEX)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.debatetimer.domain.parliamentary_debate;
package com.debatetimer.domain.parliamentary;

import com.debatetimer.domain.BoxType;
import com.debatetimer.domain.Stance;
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/com/debatetimer/dto/member/MemberCreateRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.debatetimer.dto.member;

import com.debatetimer.domain.member.Member;
import io.swagger.v3.oas.annotations.media.Schema;

public record MemberCreateRequest(
@Schema(description = "멤버 닉네임", example = "콜리")
String nickname
) {

public Member toMember() {
return new Member(nickname);
}
}
17 changes: 17 additions & 0 deletions src/main/java/com/debatetimer/dto/member/MemberCreateResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.debatetimer.dto.member;

import com.debatetimer.domain.member.Member;
import io.swagger.v3.oas.annotations.media.Schema;

public record MemberCreateResponse(
@Schema(description = "멤버 아이디", example = "1")
long id,

@Schema(description = "멤버 닉네임", example = "콜리")
String nickname
) {

public MemberCreateResponse(Member member) {
this(member.getId(), member.getNickname());
}
}
20 changes: 20 additions & 0 deletions src/main/java/com/debatetimer/dto/member/TableResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.debatetimer.dto.member;

import com.debatetimer.domain.parliamentary.ParliamentaryTable;
import io.swagger.v3.oas.annotations.media.Schema;

public record TableResponse(
@Schema(description = "테이블 이름", example = "테이블1")
String name,

@Schema(description = "토론 타입", example = "PARLIAMENTARY")
TableType type,

@Schema(description = "소요 시간 (초 단위)", example = "1800")
int duration
) {

public TableResponse(ParliamentaryTable parliamentaryTable) {
this(parliamentaryTable.getName(), TableType.PARLIAMENTARY, parliamentaryTable.getDuration());
}
}
22 changes: 22 additions & 0 deletions src/main/java/com/debatetimer/dto/member/TableResponses.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.debatetimer.dto.member;

import com.debatetimer.domain.parliamentary.ParliamentaryTable;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;

public record TableResponses(
@ArraySchema(schema = @Schema(description = "테이블들", implementation = TableResponse.class))
List<TableResponse> tables
) {

public static TableResponses from(List<ParliamentaryTable> parliamentaryTables) {
return new TableResponses(toTableResponses(parliamentaryTables));
}

private static List<TableResponse> toTableResponses(List<ParliamentaryTable> parliamentaryTables) {
return parliamentaryTables.stream()
.map(TableResponse::new)
.toList();
}
}
7 changes: 7 additions & 0 deletions src/main/java/com/debatetimer/dto/member/TableType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.debatetimer.dto.member;

public enum TableType {

PARLIAMENTARY,
;
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.debatetimer.repository.member;

import com.debatetimer.domain.member.Member;
import org.springframework.data.jpa.repository.JpaRepository;

public interface MemberRepository extends JpaRepository<Member, Long> {

default Member getById(Long id) {
return findById(id).orElseThrow(() -> new IllegalArgumentException("해당 회원이 존재하지 않습니다"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.debatetimer.repository.parliamentary;

import com.debatetimer.domain.member.Member;
import com.debatetimer.domain.parliamentary.ParliamentaryTable;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ParliamentaryTableRepository extends JpaRepository<ParliamentaryTable, Long> {

List<ParliamentaryTable> findAllByMember(Member member);
}
Empty file.
34 changes: 34 additions & 0 deletions src/main/java/com/debatetimer/service/member/MemberService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.debatetimer.service.member;

import com.debatetimer.dto.member.MemberCreateRequest;
import com.debatetimer.dto.member.MemberCreateResponse;
import com.debatetimer.dto.member.TableResponses;
import com.debatetimer.domain.member.Member;
import com.debatetimer.domain.parliamentary.ParliamentaryTable;
import com.debatetimer.repository.member.MemberRepository;
import com.debatetimer.repository.parliamentary.ParliamentaryTableRepository;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class MemberService {

private final MemberRepository memberRepository;
private final ParliamentaryTableRepository parliamentaryTableRepository;

@Transactional(readOnly = true)
public TableResponses getTables(Long memberId) {
Member member = memberRepository.getById(memberId);
List<ParliamentaryTable> parliamentaryTable = parliamentaryTableRepository.findAllByMember(member);
return TableResponses.from(parliamentaryTable);
}

@Transactional
public MemberCreateResponse createMember(MemberCreateRequest request) {
Member member = memberRepository.save(request.toMember());
return new MemberCreateResponse(member);
}
}
9 changes: 9 additions & 0 deletions src/test/java/com/debatetimer/BaseControllerTest.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.debatetimer;

import com.debatetimer.repository.member.MemberRepository;
import com.debatetimer.repository.parliamentary.ParliamentaryTableRepository;
import io.restassured.RestAssured;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;

Expand All @@ -17,4 +20,10 @@ public abstract class BaseControllerTest {
void setPort() {
RestAssured.port = port;
}

@Autowired
protected MemberRepository memberRepository;

@Autowired
protected ParliamentaryTableRepository parliamentaryTableRepository;
}
9 changes: 9 additions & 0 deletions src/test/java/com/debatetimer/BaseServiceTest.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
package com.debatetimer;

import com.debatetimer.repository.member.MemberRepository;
import com.debatetimer.repository.parliamentary.ParliamentaryTableRepository;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@ExtendWith(DataBaseCleaner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public abstract class BaseServiceTest {

@Autowired
protected MemberRepository memberRepository;

@Autowired
protected ParliamentaryTableRepository parliamentaryTableRepository;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.debatetimer.controller.member;

import static org.assertj.core.api.Assertions.assertThat;

import com.debatetimer.BaseControllerTest;
import com.debatetimer.dto.member.MemberCreateRequest;
import com.debatetimer.dto.member.MemberCreateResponse;
import com.debatetimer.dto.member.TableResponses;
import com.debatetimer.domain.member.Member;
import com.debatetimer.domain.parliamentary.ParliamentaryTable;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

class MemberControllerTest extends BaseControllerTest {

@Nested
class CreateMember {

@Test
void 회원을_생성한다() {
MemberCreateRequest request = new MemberCreateRequest("커찬");

MemberCreateResponse response = RestAssured.given().log().all()
.contentType(ContentType.JSON)
.body(request)
.when().post("/api/member")
.then().log().all()
.statusCode(201)
.extract().as(MemberCreateResponse.class);

assertThat(response.nickname()).isEqualTo(request.nickname());
}
}

@Nested
class getTables {

@Test
void 회원의_전체_토론_시간표를_조회한다() {
Member member = memberRepository.save(new Member("커찬"));
parliamentaryTableRepository.save(new ParliamentaryTable(member, "토론 시간표 A", "주제", 1800));
parliamentaryTableRepository.save(new ParliamentaryTable(member, "토론 시간표 B", "주제", 1900));

TableResponses response = RestAssured.given().log().all()
.contentType(ContentType.JSON)
.queryParam("memberId", member.getId())
.when().get("/api/table")
.then().log().all()
.statusCode(200)
.extract().as(TableResponses.class);

assertThat(response.tables()).hasSize(2);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.debatetimer.domain;
package com.debatetimer.domain.member;

import org.junit.jupiter.api.Nested;
import org.junit.jupiter.params.ParameterizedTest;
Expand Down
Loading

0 comments on commit 09287ab

Please sign in to comment.