Migration done for the backend

This commit is contained in:
Hubert Van De Walle 2020-04-25 18:17:34 +02:00
parent 52aae6773f
commit 69ede50a59
6 changed files with 65 additions and 37 deletions

View File

@ -5,9 +5,9 @@ drop table if exists Notes;
CREATE TABLE `Notes` CREATE TABLE `Notes`
( (
`uuid` binary(16) PRIMARY KEY, `uuid` binary(16) PRIMARY KEY,
`title` varchar(50) NOT NULL, `title` varchar(50) NOT NULL,
`user_id` int NOT NULL, `user_id` int NOT NULL,
`updated_at` datetime `updated_at` datetime
); );
@ -27,9 +27,10 @@ ALTER TABLE `Tags`
CREATE TABLE `Chapters` CREATE TABLE `Chapters`
( (
`id` int PRIMARY KEY AUTO_INCREMENT, `id` int PRIMARY KEY AUTO_INCREMENT,
`number` int NOT NULL, `number` int NOT NULL,
`content` text NOT NULL, `title` varchar(50) NOT NULL,
`note_uuid` binary(16) NOT NULL `content` text NOT NULL,
`note_uuid` binary(16) NOT NULL
); );
ALTER TABLE `Chapters` ALTER TABLE `Chapters`

View File

@ -4,7 +4,7 @@
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> <pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder> </encoder>
</appender> </appender>
<root level="INFO"> <root level="TRACE">
<appender-ref ref="STDOUT"/> <appender-ref ref="STDOUT"/>
</root> </root>
<logger name="org.eclipse.jetty" level="INFO"/> <logger name="org.eclipse.jetty" level="INFO"/>

View File

@ -1,7 +1,8 @@
package be.vandewalleh.extensions package be.vandewalleh.extensions
import be.vandewalleh.kodein import be.vandewalleh.kodein
import be.vandewalleh.services.FullNoteDTOPatch import be.vandewalleh.services.FullNoteCreateDTO
import be.vandewalleh.services.FullNotePatchDTO
import be.vandewalleh.services.UserService import be.vandewalleh.services.UserService
import io.ktor.application.* import io.ktor.application.*
import io.ktor.auth.* import io.ktor.auth.*
@ -26,6 +27,6 @@ fun ApplicationCall.userId(): Int {
class NoteCreate(val title: String, val tags: List<String>) class NoteCreate(val title: String, val tags: List<String>)
suspend fun ApplicationCall.receiveNoteCreate(): NoteCreate = receive() suspend fun ApplicationCall.receiveNoteCreate(): FullNoteCreateDTO = receive()
suspend fun ApplicationCall.receiveNotePatch() : FullNoteDTOPatch = receive() suspend fun ApplicationCall.receiveNotePatch() : FullNotePatchDTO = receive()

View File

@ -1,8 +1,6 @@
package be.vandewalleh.routing package be.vandewalleh.routing
import be.vandewalleh.extensions.noteTitle
import be.vandewalleh.extensions.receiveNoteCreate import be.vandewalleh.extensions.receiveNoteCreate
import be.vandewalleh.extensions.respondStatus
import be.vandewalleh.extensions.userId import be.vandewalleh.extensions.userId
import be.vandewalleh.services.NotesService import be.vandewalleh.services.NotesService
import io.ktor.application.* import io.ktor.application.*
@ -25,16 +23,9 @@ fun Routing.notes(kodein: Kodein) {
post("/notes") { post("/notes") {
val userId = call.userId() val userId = call.userId()
val noteUuid = call.parameters.noteTitle()
val note = call.receiveNoteCreate() val note = call.receiveNoteCreate()
val exists = notesService.noteExistsWithTitle(userId, note.title) val uuid = notesService.createNote(userId, note)
call.respond(HttpStatusCode.Created, mapOf("uuid" to uuid))
if (exists) {
return@post call.respondStatus(HttpStatusCode.Conflict)
}
notesService.createNote(userId, note.title, note.tags)
call.respondStatus(HttpStatusCode.Created)
} }
} }
} }

View File

@ -23,7 +23,7 @@ fun Routing.title(kodein: Kodein) {
val noteUuid = call.parameters.noteUuid() val noteUuid = call.parameters.noteUuid()
val exists = notesService.noteExists(userId, noteUuid) val exists = notesService.noteExists(userId, noteUuid)
if (exists) return@get call.respondStatus(HttpStatusCode.NotFound) if (!exists) return@get call.respondStatus(HttpStatusCode.NotFound)
val response = notesService.getNote(noteUuid) val response = notesService.getNote(noteUuid)
call.respond(response) call.respond(response)

View File

@ -29,7 +29,7 @@ class NotesService(override val kodein: Kodein) : KodeinAware {
.sortedByDescending { it.updatedAt } .sortedByDescending { it.updatedAt }
.toList() .toList()
if(notes.isEmpty()) return emptyList() if (notes.isEmpty()) return emptyList()
val tags = db.sequenceOf(Tags) val tags = db.sequenceOf(Tags)
.filterColumns { listOf(it.noteUuid, it.name) } .filterColumns { listOf(it.noteUuid, it.name) }
@ -46,37 +46,54 @@ class NotesService(override val kodein: Kodein) : KodeinAware {
} }
} }
fun noteExistsWithTitle(userId: Int, title: String): Boolean {
TODO()
}
fun noteExists(userId: Int, uuid: UUID): Boolean { fun noteExists(userId: Int, uuid: UUID): Boolean {
TODO() return db.from(Notes)
.select(Notes.uuid)
.where { Notes.userId eq userId }
.where { Notes.uuid eq uuid }
.limit(0, 1)
.toList().size == 1
} }
fun createNote(userId: Int, title: String, tags: List<String>) { fun createNote(userId: Int, note: FullNoteCreateDTO): UUID {
val uuid = UUID.randomUUID()
db.useTransaction { db.useTransaction {
val uuid = UUID.randomUUID()
db.insert(Notes) { db.insert(Notes) {
it.uuid to uuid it.uuid to uuid
it.title to title it.title to note.title
it.userId to userId it.userId to userId
it.updatedAt to LocalDateTime.now() it.updatedAt to LocalDateTime.now()
} }
db.batchInsert(Tags) { db.batchInsert(Tags) {
tags.forEach { tagName -> note.tags.forEach { tagName ->
item { item {
it.noteUuid to uuid it.noteUuid to uuid
it.name to tagName it.name to tagName
} }
} }
} }
db.batchInsert(Chapters) {
note.chapters.forEachIndexed { index, chapter ->
item {
it.noteUuid to uuid
it.title to chapter.title
it.number to index
it.content to chapter.content
}
}
}
} }
return uuid
} }
fun getNote(noteUuid: UUID): FullNoteDTO { fun getNote(noteUuid: UUID): FullNoteDTO {
TODO() val note = db.sequenceOf(Notes)
.filterColumns { listOf(it.title, it.updatedAt) }
.find { it.uuid eq noteUuid } ?: error("Note not found")
val tags = db.from(Tags) val tags = db.from(Tags)
.select(Tags.name) .select(Tags.name)
.where { Tags.noteUuid eq noteUuid } .where { Tags.noteUuid eq noteUuid }
@ -90,10 +107,18 @@ class NotesService(override val kodein: Kodein) : KodeinAware {
.map { ChapterDTO(it[Chapters.title]!!, it[Chapters.content]!!) } .map { ChapterDTO(it[Chapters.title]!!, it[Chapters.content]!!) }
.toList() .toList()
val updatedAtFormatted = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(note.updatedAt)
return FullNoteDTO(
uuid = noteUuid,
title = note.title,
updatedAt = updatedAtFormatted,
tags = tags,
chapters = chapters
)
} }
fun updateNote(patch: FullNoteDTOPatch) { fun updateNote(patch: FullNotePatchDTO) {
if(patch.uuid == null) return if (patch.uuid == null) return
db.useTransaction { db.useTransaction {
if (patch.title != null) { if (patch.title != null) {
db.update(Notes) { db.update(Notes) {
@ -134,7 +159,11 @@ class NotesService(override val kodein: Kodein) : KodeinAware {
.map { it[Tags.name]!! } .map { it[Tags.name]!! }
} }
data class ChapterDTO(val title: String, val content: String) data class ChapterDTO(
val title: String,
val content: String
)
data class FullNoteDTO( data class FullNoteDTO(
val uuid: UUID, val uuid: UUID,
val title: String, val title: String,
@ -143,7 +172,13 @@ data class FullNoteDTO(
val chapters: List<ChapterDTO> val chapters: List<ChapterDTO>
) )
data class FullNoteDTOPatch( data class FullNoteCreateDTO(
val title: String,
val tags: List<String>,
val chapters: List<ChapterDTO>
)
data class FullNotePatchDTO(
val uuid: UUID? = null, val uuid: UUID? = null,
val title: String? = null, val title: String? = null,
val updatedAt: String? = null, val updatedAt: String? = null,