-
Notifications
You must be signed in to change notification settings - Fork 4
Feature/#424 캘린더 도메인에 필요한 api를 개발한다 #438
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
base: develop
Are you sure you want to change the base?
The head ref may contain hidden characters: "Feature/#424-\uCE98\uB9B0\uB354-\uB3C4\uBA54\uC778\uC5D0-\uD544\uC694\uD55C-API\uB97C-\uAC1C\uBC1C\uD55C\uB2E4"
Changes from 7 commits
dfe48f8
c24bedd
adc2d80
01b860b
bb2c00d
f5e5dff
32c38e3
6e9810a
6124ed1
781e280
ed892e3
fc71cb6
126c7dd
ad4a22d
c75eb4f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| package com.keeper.homepage.domain.calendar.api | ||
|
|
||
| import com.keeper.homepage.domain.calendar.application.ScheduleService | ||
| import com.keeper.homepage.domain.calendar.dto.request.SaveScheduleRequest | ||
| import jakarta.validation.Valid | ||
| import lombok.RequiredArgsConstructor | ||
| import org.springframework.http.ResponseEntity | ||
| import org.springframework.stereotype.Controller | ||
| import org.springframework.validation.annotation.Validated | ||
| import org.springframework.web.bind.annotation.PostMapping | ||
| import org.springframework.web.bind.annotation.RequestBody | ||
| import org.springframework.web.bind.annotation.RequestMapping | ||
| import org.springframework.web.bind.annotation.RestController | ||
|
|
||
| @Validated | ||
| @RestController | ||
| @RequestMapping("/schedule") | ||
| @RequiredArgsConstructor | ||
| class ScheduleController( | ||
| val scheduleService: ScheduleService, | ||
| ) { | ||
|
|
||
| @PostMapping | ||
| fun saveSchedule(@RequestBody @Valid saveScheduleRequest: SaveScheduleRequest): ResponseEntity<Void> { | ||
| scheduleService.saveSchedule( | ||
| saveScheduleRequest.name, | ||
| saveScheduleRequest.startTime, | ||
| saveScheduleRequest.endTime, | ||
| saveScheduleRequest.scheduleTypeId | ||
| ) | ||
|
|
||
| return ResponseEntity.ok().build() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 데이터를 생성하는 api이니, 201 Created 응답코드를 반환하는 건 어떨까요?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아 그렇네요 감사합니다 |
||
| } | ||
|
|
||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,36 @@ | ||||||||||||||||||||
| package com.keeper.homepage.domain.calendar.application | ||||||||||||||||||||
|
|
||||||||||||||||||||
| import com.keeper.homepage.domain.Schedule.entity.Schedule | ||||||||||||||||||||
| import com.keeper.homepage.domain.calendar.dao.ScheduleRepository | ||||||||||||||||||||
| import com.keeper.homepage.domain.calendar.dao.ScheduleTypeRepository | ||||||||||||||||||||
| import lombok.RequiredArgsConstructor | ||||||||||||||||||||
| import org.springframework.data.repository.findByIdOrNull | ||||||||||||||||||||
| import org.springframework.stereotype.Service | ||||||||||||||||||||
| import org.springframework.transaction.annotation.Transactional | ||||||||||||||||||||
| import java.time.LocalDateTime | ||||||||||||||||||||
|
|
||||||||||||||||||||
| @Service | ||||||||||||||||||||
| @RequiredArgsConstructor | ||||||||||||||||||||
| @Transactional(readOnly = true) | ||||||||||||||||||||
| class ScheduleService( | ||||||||||||||||||||
| val scheduleRepository: ScheduleRepository, | ||||||||||||||||||||
| val scheduleTypeRepository: ScheduleTypeRepository, | ||||||||||||||||||||
| ) { | ||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 코틀린에서는
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 보름님 말씀대로 RequiredArgsConstructor 필요 없습니다!
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. GOD Kt.. |
||||||||||||||||||||
|
|
||||||||||||||||||||
| @Transactional | ||||||||||||||||||||
| fun saveSchedule(name: String, startTime: LocalDateTime, | ||||||||||||||||||||
| endTime: LocalDateTime, scheduleTypeId: Long, | ||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 개인적으로 편의상 endTime의 default 값을 startTime + 1로 잡아두는 것은 어떨까욤?? 코틀린은 가능합니다!!
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 그렇네요.. 보통 디폴트 날짜는 보통 하루겠네요~ 감사합니다 |
||||||||||||||||||||
| ): Long { | ||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
너무나 사소하지만... 저는 이렇게 포맷팅하지 않으면 불편한 병에 걸렸습니다 ㅎㅎ 🤣 (코틀린 권장 스타일이긴 하더라구요!)
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저도 이 부분을 5번 정도 지웠다 했는데 다시 보니 코틀린 권장 스타일이 좋을 것 같습니다 bb 감사합니다 |
||||||||||||||||||||
| val findSchedule = scheduleTypeRepository.findByIdOrNull(scheduleTypeId) | ||||||||||||||||||||
| ?: throw IllegalArgumentException("ScheduleType not found") | ||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: 다른 코드들과 통일성 있게 커스텀 예외를 등록해두는 건 어떨까요? |
||||||||||||||||||||
|
|
||||||||||||||||||||
| return scheduleRepository.save( | ||||||||||||||||||||
| Schedule( | ||||||||||||||||||||
| name = name, | ||||||||||||||||||||
| startTime = startTime, | ||||||||||||||||||||
| endTime = endTime, | ||||||||||||||||||||
| scheduleType = findSchedule, | ||||||||||||||||||||
| ) | ||||||||||||||||||||
| ).id!! | ||||||||||||||||||||
|
Comment on lines
+32
to
+39
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.keeper.homepage.domain.calendar.dao | ||
|
|
||
| import com.keeper.homepage.domain.Schedule.entity.Schedule | ||
| import org.springframework.data.jpa.repository.JpaRepository | ||
|
|
||
| interface ScheduleRepository: JpaRepository<Schedule, Long> { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.keeper.homepage.domain.calendar.dao | ||
|
|
||
| import com.keeper.homepage.domain.calendar.entity.ScheduleType | ||
| import org.springframework.data.jpa.repository.JpaRepository | ||
|
|
||
| interface ScheduleTypeRepository: JpaRepository<ScheduleType, Long> { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package com.keeper.homepage.domain.calendar.dto.request | ||
|
|
||
| import com.fasterxml.jackson.annotation.JsonFormat | ||
| import jakarta.validation.constraints.NotEmpty | ||
| import jakarta.validation.constraints.Pattern | ||
| import java.time.LocalDateTime | ||
|
|
||
| data class SaveScheduleRequest( | ||
|
|
||
| @NotEmpty(message = "일정 이름을 입력해주세요.") | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이거 혹시 동작하나요?? 코틀린은 |
||
| val name: String, | ||
|
|
||
| @JsonFormat(pattern = "yyyy-MM-dd`T`HH:mm:ss") | ||
| val startTime: LocalDateTime, | ||
|
|
||
| @JsonFormat(pattern = "yyyy.MM.dd`T`HH:mm:ss") | ||
| val endTime: LocalDateTime, | ||
|
|
||
| @Pattern(message = "일정 타입을 입력해주세요.", regexp = "^[1234]+\$") | ||
| val scheduleTypeId: Long, | ||
| ) | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,30 @@ | ||||||
| package com.keeper.homepage.domain.Schedule.entity | ||||||
|
|
||||||
| import com.keeper.homepage.domain.calendar.entity.ScheduleType | ||||||
| import jakarta.persistence.* | ||||||
| import lombok.AccessLevel | ||||||
| import lombok.NoArgsConstructor | ||||||
| import java.time.LocalDateTime | ||||||
|
|
||||||
| @Entity | ||||||
| @Table(name = "schedule") | ||||||
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | ||||||
| class Schedule( | ||||||
|
|
||||||
| @Column(name = "name", nullable = false) | ||||||
| val name: String, | ||||||
|
|
||||||
| @Column(name = "start_time", nullable = false) | ||||||
| val startTime: LocalDateTime, | ||||||
|
|
||||||
| @Column(name = "end_time", nullable = false) | ||||||
| val endTime: LocalDateTime, | ||||||
|
|
||||||
| @JoinColumn(name = "schedule_type_id", nullable = false) | ||||||
| @OneToOne(fetch = FetchType.LAZY, cascade = [CascadeType.REMOVE]) | ||||||
| val scheduleType: ScheduleType, | ||||||
|
|
||||||
| @Id | ||||||
| @GeneratedValue(strategy = GenerationType.IDENTITY) | ||||||
| val id: Long? = null, | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Nit: 어떨까요?!
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저도 이 부분이 너무 헷갈리는데 다른 곳과 다른 분들은 어떻게 생각하시는지 궁금합니다. 아래 게시판을 보시고 의견 남겨주시면 감사하겠습니다..!!
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아하 하이버네이트가 nullable 타입을 추천하는군요... 처음 알았습니다! |
||||||
| ) | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package com.keeper.homepage.domain.calendar.entity | ||
|
|
||
| import jakarta.persistence.* | ||
| import lombok.AccessLevel | ||
| import lombok.NoArgsConstructor | ||
|
|
||
| @Entity | ||
| @Table(name = "schedule_type") | ||
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
| class ScheduleType( | ||
|
|
||
| @Column(name = "type", nullable = false) | ||
| val type: String, | ||
|
|
||
| @Column(name = "description", nullable = false) | ||
| val description: String, | ||
|
|
||
| @Id | ||
| @GeneratedValue(strategy = GenerationType.IDENTITY) | ||
| val id: Long?, | ||
| ) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. inner enum class로 타입을 명시해두면 코드 가독성이 더 올라가지 않을까 합니다!
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 좋습니다~! 감사합니다 |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| package com.keeper.homepage.domain.Schedule.application | ||
|
|
||
| import com.keeper.homepage.domain.Schedule.entity.Schedule | ||
| import com.keeper.homepage.domain.calendar.ScheduleTypeTestHelper | ||
| import com.keeper.homepage.domain.calendar.dao.ScheduleRepository | ||
| import com.keeper.homepage.domain.calendar.entity.ScheduleType | ||
| import org.springframework.beans.factory.annotation.Autowired | ||
| import org.springframework.stereotype.Component | ||
| import java.time.LocalDateTime | ||
|
|
||
| @Component | ||
| class ScheduleTestHelper { | ||
|
|
||
| @Autowired | ||
| lateinit var scheduleRepository: ScheduleRepository | ||
|
|
||
| @Autowired | ||
| lateinit var scheduleTypeTestHelper: ScheduleTypeTestHelper | ||
|
|
||
| fun generate(): Schedule { | ||
| return ScheduleBuilder().build() | ||
| } | ||
|
|
||
| fun builder(): ScheduleBuilder { | ||
| return ScheduleBuilder() | ||
| } | ||
|
|
||
| inner class ScheduleBuilder { | ||
|
|
||
| private var name: String? = null | ||
| private var startTime: LocalDateTime? = null | ||
| private var endTime: LocalDateTime? = null | ||
| private var scheduleType: ScheduleType? = null | ||
|
|
||
| private fun ScheduleBuilder() {} | ||
|
|
||
| fun name(name: String): ScheduleBuilder { | ||
| this.name = name | ||
| return this | ||
| } | ||
|
|
||
| fun startDateTime(startTime: LocalDateTime): ScheduleBuilder { | ||
| this.startTime = startTime | ||
| return this | ||
| } | ||
|
|
||
| fun endTime(endTime: LocalDateTime): ScheduleBuilder { | ||
| this.endTime = endTime | ||
| return this | ||
| } | ||
|
|
||
| fun scheduleType(scheduleType: ScheduleType): ScheduleBuilder { | ||
| this.scheduleType = scheduleType | ||
| return this | ||
| } | ||
|
|
||
| fun build(): Schedule { | ||
| return scheduleRepository.save( | ||
| Schedule( | ||
| name = name ?: "Default Name", | ||
| startTime = startTime ?: LocalDateTime.now(), | ||
| endTime = endTime ?: LocalDateTime.now(), | ||
| scheduleType = scheduleType ?: scheduleTypeTestHelper.generate(), | ||
| ) | ||
| ) | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| package com.keeper.homepage.domain.calendar | ||
|
|
||
| import com.keeper.homepage.domain.calendar.dao.ScheduleTypeRepository | ||
| import com.keeper.homepage.domain.calendar.entity.ScheduleType | ||
| import org.springframework.beans.factory.annotation.Autowired | ||
| import org.springframework.stereotype.Component | ||
| import kotlin.properties.Delegates | ||
|
|
||
| @Component | ||
| class ScheduleTypeTestHelper { | ||
|
|
||
| @Autowired | ||
| lateinit var scheduleTypeRepository: ScheduleTypeRepository | ||
|
|
||
| fun generate(): ScheduleType { | ||
| return ScheduleTypeBuilder().build() | ||
| } | ||
|
|
||
| fun builder(): ScheduleTypeBuilder { | ||
| return ScheduleTypeBuilder() | ||
| } | ||
|
|
||
| inner class ScheduleTypeBuilder { | ||
|
|
||
| private var type: String? = null | ||
| private var description: String? = null | ||
|
|
||
| private fun ScheduleTypeBuilder() {} | ||
|
|
||
| fun type(type: String): ScheduleTypeBuilder { | ||
| this.type = type | ||
| return this | ||
| } | ||
|
|
||
| fun description(description: String): ScheduleTypeBuilder { | ||
| this.description = description | ||
| return this | ||
| } | ||
|
|
||
| fun build(): ScheduleType = scheduleTypeRepository.save( | ||
| ScheduleType( | ||
| type = type ?: "일반", | ||
| description = description ?: "정기 세미나, 기술문서 발표, 1학기 시작/종료 등", | ||
| id = null | ||
| ) | ||
| ) | ||
|
|
||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package com.keeper.homepage.domain.schedule.application | ||
|
|
||
| import com.keeper.homepage.IntegrationTest | ||
| import org.junit.jupiter.api.Assertions.* | ||
| import org.junit.jupiter.api.Nested | ||
| import org.junit.jupiter.api.Test | ||
| import org.springframework.data.repository.findByIdOrNull | ||
| import java.time.LocalDateTime | ||
|
|
||
| class scheduleServiceTest: IntegrationTest() { | ||
|
|
||
| companion object { | ||
| const val DEFAULT_SCHEDULE_NAME = "Default Name" | ||
| const val DEFAULT_SCHEDULE_TYPE = "일반" | ||
| const val DEFAULT_SCHEDULE_TYPE_DESCRIPTION = "정기 세미나, 기술문서 발표, 1학기 시작/종료 등" | ||
| } | ||
|
|
||
| @Nested | ||
| inner class `캘린더 저장` { | ||
|
|
||
| @Test | ||
| fun `캘린더 저장을 성공해야 한다`() { | ||
| val startTime = LocalDateTime.now() | ||
| val endTime = startTime.plusDays(1) | ||
|
|
||
| val savedScheduleId = scheduleService.saveSchedule(DEFAULT_SCHEDULE_NAME, startTime, endTime, 1L) | ||
| val findSchedule = scheduleRepository.findByIdOrNull(savedScheduleId) ?: fail("캘린더 저장 실패") | ||
|
|
||
| assertEquals(findSchedule.name, DEFAULT_SCHEDULE_NAME) | ||
| assertEquals(findSchedule.startTime, startTime) | ||
| assertEquals(findSchedule.endTime, endTime) | ||
| assertEquals(findSchedule.scheduleType.type, DEFAULT_SCHEDULE_TYPE) | ||
| assertEquals(findSchedule.scheduleType.description, DEFAULT_SCHEDULE_TYPE_DESCRIPTION) | ||
| } | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,7 @@ | |
| import java.nio.file.Files; | ||
| import java.nio.file.Path; | ||
| import javax.imageio.ImageIO; | ||
| import org.junit.jupiter.api.Disabled; | ||
| import org.junit.jupiter.api.DisplayName; | ||
| import org.junit.jupiter.api.Nested; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
@@ -82,6 +83,7 @@ void should_resizingWell_when_validMultipartFile() throws IOException { | |
| } | ||
|
|
||
| @Test | ||
| @Disabled | ||
| @DisplayName("썸네일 저장 시 type이 null이면 NullPointerException을 발생시킨다.") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 테스트는 왜 @disabled 처리 하셨나용?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 퀵스타트 만들 때도 그렇고 항상 이 테스트가 깨졌습니다. 다른 분들은 통과 하시는지 궁금하네요..!! 메서드 들어가보면 null이면 NPE을 throw 하는게 아니라 optional empty로 반환해서 테스트가 깨지는 것 같습니다!
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 엇 뭐지 제 쪽에서는 다 통과하는데 한번 더 확인해보겠습니다 |
||
| void should_throwNullPointerException_when_parameterIsNull() { | ||
| assertThatThrownBy(() -> thumbnailUtil.saveThumbnail(null, null)) | ||
|
|
||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
복수형 어떨까욥!