package be.vandewalleh.services import be.vandewalleh.tables.Chapters import be.vandewalleh.tables.Notes import be.vandewalleh.tables.Tags import me.liuwj.ktorm.database.* import me.liuwj.ktorm.dsl.* import me.liuwj.ktorm.entity.* import org.kodein.di.Kodein import org.kodein.di.KodeinAware import org.kodein.di.generic.instance import java.time.LocalDateTime import java.time.format.DateTimeFormatter import java.util.* /** * service to handle database queries at the Notes level. */ class NotesService(override val kodein: Kodein) : KodeinAware { private val db by instance() /** * returns a list of [BasicNoteDTO] associated with the userId */ fun getNotes(userId: Int): List { val notes = db.sequenceOf(Notes) .filterColumns { listOf(it.uuid, it.title, it.updatedAt) } .filter { it.userId eq userId } .sortedByDescending { it.updatedAt } .toList() if (notes.isEmpty()) return emptyList() val tags = db.sequenceOf(Tags) .filterColumns { listOf(it.noteUuid, it.name) } .filter { it.noteUuid inList notes.map { it.uuid } } .toList() return notes.map { note -> val noteTags = tags.asSequence() .filter { it.note.uuid == note.uuid } .map { it.name } .toList() BasicNoteDTO(note.uuid, note.title, noteTags, DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(note.updatedAt)) } } fun noteExists(userId: Int, uuid: UUID): Boolean { 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, note: FullNoteCreateDTO): UUID { val uuid = UUID.randomUUID() db.useTransaction { db.insert(Notes) { it.uuid to uuid it.title to note.title it.userId to userId it.updatedAt to LocalDateTime.now() } db.batchInsert(Tags) { note.tags.forEach { tagName -> item { it.noteUuid to uuid 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 { 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) .select(Tags.name) .where { Tags.noteUuid eq noteUuid } .map { it[Tags.name]!! } .toList() val chapters = db.from(Chapters) .select(Chapters.title, Chapters.content) .where { Chapters.noteUuid eq noteUuid } .orderBy(Chapters.number.asc()) .map { ChapterDTO(it[Chapters.title]!!, it[Chapters.content]!!) } .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: FullNotePatchDTO) { if (patch.uuid == null) return db.useTransaction { if (patch.title != null) { db.update(Notes) { it.title to patch.title it.updatedAt to LocalDateTime.now() where { it.uuid eq patch.uuid } } } if (patch.tags != null) { // delete all tags db.delete(Tags) { it.noteUuid eq patch.uuid } // put new ones patch.tags.forEach { tagName -> db.insert(Tags) { it.name to tagName it.noteUuid to patch.uuid } } } } TODO("get chapters") } fun deleteNote(noteUuid: UUID): Unit = db.useTransaction { db.delete(Notes) { it.uuid eq noteUuid } } fun getTags(userId: Int): List = db.from(Tags) .leftJoin(Notes, on = Tags.noteUuid eq Notes.uuid) .select(Tags.name) .where { Notes.userId eq userId } .map { it[Tags.name]!! } } data class ChapterDTO( val title: String, val content: String ) data class FullNoteDTO( val uuid: UUID, val title: String, val updatedAt: String, val tags: List, val chapters: List ) data class FullNoteCreateDTO( val title: String, val tags: List, val chapters: List ) data class FullNotePatchDTO( val uuid: UUID? = null, val title: String? = null, val updatedAt: String? = null, val tags: List? = null, val chapters: List? = null ) data class BasicNoteDTO( val uuid: UUID, val title: String, val tags: List, val updatedAt: String )