feat(api): summary field for read lists

closes #558
This commit is contained in:
Gauthier Roebroeck 2021-07-30 15:59:53 +08:00
parent aaa4555137
commit 1148e46d90
10 changed files with 37 additions and 4 deletions

View file

@ -0,0 +1,2 @@
alter table readlist
add column SUMMARY varchar not NULL default '';

View file

@ -7,6 +7,7 @@ import java.util.SortedMap
data class ReadList( data class ReadList(
val name: String, val name: String,
val summary: String = "",
val bookIds: SortedMap<Int, String> = sortedMapOf(), val bookIds: SortedMap<Int, String> = sortedMapOf(),

View file

@ -187,6 +187,7 @@ class ReadListDao(
dsl.insertInto(rl) dsl.insertInto(rl)
.set(rl.ID, readList.id) .set(rl.ID, readList.id)
.set(rl.NAME, readList.name) .set(rl.NAME, readList.name)
.set(rl.SUMMARY, readList.summary)
.set(rl.BOOK_COUNT, readList.bookIds.size) .set(rl.BOOK_COUNT, readList.bookIds.size)
.execute() .execute()
@ -207,6 +208,7 @@ class ReadListDao(
override fun update(readList: ReadList) { override fun update(readList: ReadList) {
dsl.update(rl) dsl.update(rl)
.set(rl.NAME, readList.name) .set(rl.NAME, readList.name)
.set(rl.SUMMARY, readList.summary)
.set(rl.BOOK_COUNT, readList.bookIds.size) .set(rl.BOOK_COUNT, readList.bookIds.size)
.set(rl.LAST_MODIFIED_DATE, LocalDateTime.now(ZoneId.of("Z"))) .set(rl.LAST_MODIFIED_DATE, LocalDateTime.now(ZoneId.of("Z")))
.where(rl.ID.eq(readList.id)) .where(rl.ID.eq(readList.id))
@ -263,6 +265,7 @@ class ReadListDao(
private fun ReadlistRecord.toDomain(bookIds: SortedMap<Int, String>) = private fun ReadlistRecord.toDomain(bookIds: SortedMap<Int, String>) =
ReadList( ReadList(
name = name, name = name,
summary = summary,
bookIds = bookIds, bookIds = bookIds,
id = id, id = id,
createdDate = createdDate.toCurrentTimeZone(), createdDate = createdDate.toCurrentTimeZone(),

View file

@ -145,6 +145,7 @@ class ReadListController(
readListLifecycle.addReadList( readListLifecycle.addReadList(
ReadList( ReadList(
name = readList.name, name = readList.name,
summary = readList.summary,
bookIds = readList.bookIds.toIndexedMap() bookIds = readList.bookIds.toIndexedMap()
) )
).toDto() ).toDto()
@ -169,6 +170,7 @@ class ReadListController(
readListRepository.findByIdOrNull(id)?.let { existing -> readListRepository.findByIdOrNull(id)?.let { existing ->
val updated = existing.copy( val updated = existing.copy(
name = readList.name ?: existing.name, name = readList.name ?: existing.name,
summary = readList.summary ?: existing.summary,
bookIds = readList.bookIds?.toIndexedMap() ?: existing.bookIds bookIds = readList.bookIds?.toIndexedMap() ?: existing.bookIds
) )
try { try {

View file

@ -6,5 +6,6 @@ import javax.validation.constraints.NotEmpty
data class ReadListCreationDto( data class ReadListCreationDto(
@get:NotBlank val name: String, @get:NotBlank val name: String,
val summary: String = "",
@get:NotEmpty @get:UniqueElements val bookIds: List<String> @get:NotEmpty @get:UniqueElements val bookIds: List<String>
) )

View file

@ -7,6 +7,7 @@ import java.time.LocalDateTime
data class ReadListDto( data class ReadListDto(
val id: String, val id: String,
val name: String, val name: String,
val summary: String,
val bookIds: List<String>, val bookIds: List<String>,
@ -22,6 +23,7 @@ fun ReadList.toDto() =
ReadListDto( ReadListDto(
id = id, id = id,
name = name, name = name,
summary = summary,
bookIds = bookIds.values.toList(), bookIds = bookIds.values.toList(),
createdDate = createdDate.toUTC(), createdDate = createdDate.toUTC(),
lastModifiedDate = lastModifiedDate.toUTC(), lastModifiedDate = lastModifiedDate.toUTC(),

View file

@ -6,5 +6,6 @@ import org.hibernate.validator.constraints.UniqueElements
data class ReadListUpdateDto( data class ReadListUpdateDto(
@get:NullOrNotBlank val name: String?, @get:NullOrNotBlank val name: String?,
val summary: String?,
@get:NullOrNotEmpty @get:UniqueElements val bookIds: List<String>? @get:NullOrNotEmpty @get:UniqueElements val bookIds: List<String>?
) )

View file

@ -707,7 +707,7 @@ class LibraryContentLifecycleTest(
bookRepository.findByIdOrNull(book.id)?.let { bookRepository.findByIdOrNull(book.id)?.let {
bookRepository.update(it.copy(fileHash = "sameHash")) bookRepository.update(it.copy(fileHash = "sameHash"))
readListLifecycle.addReadList(ReadList("read list", listOf(it.id).toIndexedMap())) readListLifecycle.addReadList(ReadList("read list", bookIds = listOf(it.id).toIndexedMap()))
} }
every { mockHasher.computeHash(any()) } returns "sameHash" every { mockHasher.computeHash(any()) } returns "sameHash"
@ -1065,7 +1065,7 @@ class LibraryContentLifecycleTest(
bookRepository.findByIdOrNull(book2.id)?.let { bookRepository.findByIdOrNull(book2.id)?.let {
bookRepository.update(it.copy(fileHash = "sameHash")) bookRepository.update(it.copy(fileHash = "sameHash"))
readListLifecycle.addReadList(ReadList("read list", listOf(it.id).toIndexedMap())) readListLifecycle.addReadList(ReadList("read list", bookIds = listOf(it.id).toIndexedMap()))
} }
every { mockHasher.computeHash(any()) } returns "sameHash" every { mockHasher.computeHash(any()) } returns "sameHash"

View file

@ -60,6 +60,7 @@ class ReadListDaoTest(
val readList = ReadList( val readList = ReadList(
name = "MyReadList", name = "MyReadList",
summary = "summary",
bookIds = books.map { it.id }.toIndexedMap() bookIds = books.map { it.id }.toIndexedMap()
) )
@ -71,6 +72,7 @@ class ReadListDaoTest(
// then // then
assertThat(created.name).isEqualTo(readList.name) assertThat(created.name).isEqualTo(readList.name)
assertThat(created.summary).isEqualTo(readList.summary)
assertThat(created.createdDate) assertThat(created.createdDate)
.isEqualTo(created.lastModifiedDate) .isEqualTo(created.lastModifiedDate)
.isCloseTo(now, offset) .isCloseTo(now, offset)
@ -95,6 +97,7 @@ class ReadListDaoTest(
// when // when
val updatedReadList = readList.copy( val updatedReadList = readList.copy(
name = "UpdatedReadList", name = "UpdatedReadList",
summary = "summary",
bookIds = readList.bookIds.values.take(5).toIndexedMap() bookIds = readList.bookIds.values.take(5).toIndexedMap()
) )
@ -104,6 +107,7 @@ class ReadListDaoTest(
// then // then
assertThat(updated.name).isEqualTo(updatedReadList.name) assertThat(updated.name).isEqualTo(updatedReadList.name)
assertThat(updated.summary).isEqualTo(updatedReadList.summary)
assertThat(updated.createdDate).isNotEqualTo(updated.lastModifiedDate) assertThat(updated.createdDate).isNotEqualTo(updated.lastModifiedDate)
assertThat(updated.lastModifiedDate).isCloseTo(now, offset) assertThat(updated.lastModifiedDate).isCloseTo(now, offset)
assertThat(updated.bookIds.values) assertThat(updated.bookIds.values)

View file

@ -317,7 +317,7 @@ class ReadListControllerTest(
@WithMockCustomUser(roles = [ROLE_ADMIN]) @WithMockCustomUser(roles = [ROLE_ADMIN])
fun `given admin user when creating read list then return ok`() { fun `given admin user when creating read list then return ok`() {
val jsonString = """ val jsonString = """
{"name":"readlist","bookIds":["${booksLibrary1.first().id}"]} {"name":"readlist","summary":"summary","bookIds":["${booksLibrary1.first().id}"]}
""".trimIndent() """.trimIndent()
mockMvc.post("/api/v1/readlists") { mockMvc.post("/api/v1/readlists") {
@ -327,6 +327,7 @@ class ReadListControllerTest(
status { isOk() } status { isOk() }
jsonPath("$.bookIds.length()") { value(1) } jsonPath("$.bookIds.length()") { value(1) }
jsonPath("$.name") { value("readlist") } jsonPath("$.name") { value("readlist") }
jsonPath("$.summary") { value("summary") }
} }
} }
@ -386,7 +387,7 @@ class ReadListControllerTest(
makeReadLists() makeReadLists()
val jsonString = """ val jsonString = """
{"name":"updated","bookIds":["${booksLibrary1.first().id}"]} {"name":"updated","summary":"updatedSummary","bookIds":["${booksLibrary1.first().id}"]}
""".trimIndent() """.trimIndent()
mockMvc.patch("/api/v1/readlists/${rlLib1.id}") { mockMvc.patch("/api/v1/readlists/${rlLib1.id}") {
@ -400,6 +401,7 @@ class ReadListControllerTest(
.andExpect { .andExpect {
status { isOk() } status { isOk() }
jsonPath("$.name") { value("updated") } jsonPath("$.name") { value("updated") }
jsonPath("$.summary") { value("updatedSummary") }
jsonPath("$.bookIds.length()") { value(1) } jsonPath("$.bookIds.length()") { value(1) }
jsonPath("$.filtered") { value(false) } jsonPath("$.filtered") { value(false) }
} }
@ -449,6 +451,20 @@ class ReadListControllerTest(
.andExpect { .andExpect {
status { isOk() } status { isOk() }
jsonPath("$.name") { value("newName") } jsonPath("$.name") { value("newName") }
jsonPath("$.summary") { value("") }
jsonPath("$.bookIds.length()") { value(5) }
}
mockMvc.patch("/api/v1/readlists/${rlLib1.id}") {
contentType = MediaType.APPLICATION_JSON
content = """{"summary":"newSummary"}"""
}
mockMvc.get("/api/v1/readlists/${rlLib1.id}")
.andExpect {
status { isOk() }
jsonPath("$.name") { value("Lib1") }
jsonPath("$.summary") { value("newSummary") }
jsonPath("$.bookIds.length()") { value(5) } jsonPath("$.bookIds.length()") { value(5) }
} }
@ -461,6 +477,7 @@ class ReadListControllerTest(
.andExpect { .andExpect {
status { isOk() } status { isOk() }
jsonPath("$.name") { value("Lib1+2") } jsonPath("$.name") { value("Lib1+2") }
jsonPath("$.summary") { value("") }
jsonPath("$.bookIds.length()") { value(1) } jsonPath("$.bookIds.length()") { value(1) }
} }
} }