Merge branch 'feature/notes-controller'
This commit is contained in:
commit
2a142504a5
14
api/http/test.http
Normal file
14
api/http/test.http
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Register a new user
|
||||||
|
POST http://localhost:8081/login
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"username": "hubert",
|
||||||
|
"password": "test"
|
||||||
|
}
|
||||||
|
|
||||||
|
> {% client.global.set("token", response.body.token); %}
|
||||||
|
|
||||||
|
### Get notes
|
||||||
|
GET http://localhost:8081/notes
|
||||||
|
Authorization: Bearer {{token}}
|
||||||
5
api/resources/db/migration/V5__Update_note_table.sql
Normal file
5
api/resources/db/migration/V5__Update_note_table.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
ALTER TABLE `Notes`
|
||||||
|
DROP COLUMN `last_viewed`;
|
||||||
|
|
||||||
|
ALTER TABLE `Notes`
|
||||||
|
ADD COLUMN `updated_at` datetime;
|
||||||
@ -9,6 +9,7 @@ import be.vandewalleh.features.features
|
|||||||
import be.vandewalleh.migrations.Migration
|
import be.vandewalleh.migrations.Migration
|
||||||
import io.ktor.application.Application
|
import io.ktor.application.Application
|
||||||
import io.ktor.application.log
|
import io.ktor.application.log
|
||||||
|
import io.ktor.auth.authenticate
|
||||||
import io.ktor.routing.routing
|
import io.ktor.routing.routing
|
||||||
import me.liuwj.ktorm.database.Database
|
import me.liuwj.ktorm.database.Database
|
||||||
import org.kodein.di.Kodein
|
import org.kodein.di.Kodein
|
||||||
|
|||||||
31
api/src/controllers/AuthCrudController.kt
Normal file
31
api/src/controllers/AuthCrudController.kt
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package be.vandewalleh.controllers
|
||||||
|
|
||||||
|
import io.ktor.application.ApplicationCall
|
||||||
|
import io.ktor.auth.UserIdPrincipal
|
||||||
|
import io.ktor.auth.authenticate
|
||||||
|
import io.ktor.auth.principal
|
||||||
|
import io.ktor.routing.Route
|
||||||
|
import io.ktor.routing.Routing
|
||||||
|
import io.ktor.routing.route
|
||||||
|
import org.kodein.di.Kodein
|
||||||
|
|
||||||
|
abstract class AuthCrudController(
|
||||||
|
private val path: String,
|
||||||
|
override val kodein: Kodein
|
||||||
|
) :
|
||||||
|
KodeinController(kodein) {
|
||||||
|
|
||||||
|
abstract val route: Route.() -> Unit
|
||||||
|
|
||||||
|
fun ApplicationCall.userEmail(): String =
|
||||||
|
this.principal<UserIdPrincipal>()!!.name
|
||||||
|
|
||||||
|
override fun Routing.registerRoutes() {
|
||||||
|
authenticate {
|
||||||
|
route(path) {
|
||||||
|
route()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -14,5 +14,5 @@ val controllerModule = Kodein.Module(name = "Controller") {
|
|||||||
|
|
||||||
bind<KodeinController>().inSet() with singleton { UserController(this.kodein) }
|
bind<KodeinController>().inSet() with singleton { UserController(this.kodein) }
|
||||||
bind<KodeinController>().inSet() with singleton { HealthCheckController(this.kodein) }
|
bind<KodeinController>().inSet() with singleton { HealthCheckController(this.kodein) }
|
||||||
bind<KodeinController>().inSet() with singleton { NoteController(this.kodein) }
|
bind<KodeinController>().inSet() with singleton { NotesController(this.kodein) }
|
||||||
}
|
}
|
||||||
@ -1,27 +1,14 @@
|
|||||||
package be.vandewalleh.controllers
|
package be.vandewalleh.controllers
|
||||||
|
|
||||||
import io.ktor.application.Application
|
|
||||||
import io.ktor.locations.locations
|
|
||||||
import io.ktor.routing.Routing
|
import io.ktor.routing.Routing
|
||||||
import org.kodein.di.Kodein
|
import org.kodein.di.Kodein
|
||||||
import org.kodein.di.KodeinAware
|
import org.kodein.di.KodeinAware
|
||||||
import org.kodein.di.generic.instance
|
|
||||||
|
|
||||||
abstract class KodeinController(override val kodein: Kodein) : KodeinAware {
|
abstract class KodeinController(override val kodein: Kodein) : KodeinAware {
|
||||||
/**
|
|
||||||
* Injected dependency with the current [Application].
|
|
||||||
*/
|
|
||||||
val application: Application by instance()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shortcut to get the url of a [TypedRoute].
|
|
||||||
*/
|
|
||||||
val TypedRoute.href get() = application.locations.href(this)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method that subtypes must override to register the handled [Routing] routes.
|
* Method that subtypes must override to register the handled [Routing] routes.
|
||||||
*/
|
*/
|
||||||
abstract fun Routing.registerRoutes()
|
abstract fun Routing.registerRoutes()
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TypedRoute
|
|
||||||
@ -1,73 +0,0 @@
|
|||||||
package be.vandewalleh.controllers
|
|
||||||
|
|
||||||
import be.vandewalleh.entities.Note
|
|
||||||
import be.vandewalleh.entities.Tag
|
|
||||||
import be.vandewalleh.errors.ApiError
|
|
||||||
import be.vandewalleh.tables.Notes
|
|
||||||
import be.vandewalleh.tables.Tags
|
|
||||||
import be.vandewalleh.tables.Users
|
|
||||||
import io.ktor.application.call
|
|
||||||
import io.ktor.auth.UserIdPrincipal
|
|
||||||
import io.ktor.auth.authenticate
|
|
||||||
import io.ktor.auth.principal
|
|
||||||
import io.ktor.http.HttpStatusCode
|
|
||||||
import io.ktor.locations.Location
|
|
||||||
import io.ktor.locations.post
|
|
||||||
import io.ktor.request.receive
|
|
||||||
import io.ktor.response.respond
|
|
||||||
import io.ktor.routing.Routing
|
|
||||||
import me.liuwj.ktorm.database.Database
|
|
||||||
import me.liuwj.ktorm.dsl.eq
|
|
||||||
import me.liuwj.ktorm.entity.add
|
|
||||||
import me.liuwj.ktorm.entity.find
|
|
||||||
import me.liuwj.ktorm.entity.sequenceOf
|
|
||||||
import org.kodein.di.Kodein
|
|
||||||
import org.kodein.di.generic.instance
|
|
||||||
|
|
||||||
class NoteController(kodein: Kodein) : KodeinController(kodein) {
|
|
||||||
private val db by instance<Database>()
|
|
||||||
|
|
||||||
override fun Routing.registerRoutes() {
|
|
||||||
authenticate {
|
|
||||||
post<Routes.Notes> {
|
|
||||||
data class Response(val message: String)
|
|
||||||
|
|
||||||
val (email) = call.principal<UserIdPrincipal>()!!
|
|
||||||
val body = call.receive<PostNotesBody>()
|
|
||||||
|
|
||||||
val user = db.sequenceOf(Users)
|
|
||||||
.find { Users.email eq email }
|
|
||||||
?: return@post call.respond(HttpStatusCode.Forbidden, ApiError.DeletedUserError)
|
|
||||||
|
|
||||||
val transaction = db.useTransaction {
|
|
||||||
|
|
||||||
val note = Note {
|
|
||||||
this.title = body.title
|
|
||||||
this.user = user
|
|
||||||
}
|
|
||||||
|
|
||||||
db.sequenceOf(Notes).add(note)
|
|
||||||
|
|
||||||
body.tags.forEach { tagName ->
|
|
||||||
val tag = Tag {
|
|
||||||
this.name = tagName
|
|
||||||
this.note = note
|
|
||||||
}
|
|
||||||
db.sequenceOf(Tags).add(tag)
|
|
||||||
}
|
|
||||||
1
|
|
||||||
}
|
|
||||||
|
|
||||||
return@post call.respond(Response("created"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
object Routes {
|
|
||||||
@Location("/notes")
|
|
||||||
class Notes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class PostNotesBody(val title: String, val content: String, val tags: List<String>)
|
|
||||||
59
api/src/controllers/NotesController.kt
Normal file
59
api/src/controllers/NotesController.kt
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package be.vandewalleh.controllers
|
||||||
|
|
||||||
|
import be.vandewalleh.tables.Notes
|
||||||
|
import be.vandewalleh.tables.Tags
|
||||||
|
import be.vandewalleh.tables.Users
|
||||||
|
import io.ktor.application.call
|
||||||
|
import io.ktor.response.respond
|
||||||
|
import io.ktor.response.respondText
|
||||||
|
import io.ktor.routing.Route
|
||||||
|
import io.ktor.routing.get
|
||||||
|
import me.liuwj.ktorm.database.Database
|
||||||
|
import me.liuwj.ktorm.dsl.*
|
||||||
|
import org.kodein.di.Kodein
|
||||||
|
import org.kodein.di.generic.instance
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
class NotesController(kodein: Kodein) : AuthCrudController("/notes", kodein) {
|
||||||
|
private val db by kodein.instance<Database>()
|
||||||
|
|
||||||
|
private class ResponseItem(val title: String, val tags: List<String>, val updatedAt: String)
|
||||||
|
|
||||||
|
override val route: Route.() -> Unit = {
|
||||||
|
get {
|
||||||
|
val email = call.userEmail()
|
||||||
|
|
||||||
|
val list = db.from(Notes)
|
||||||
|
.leftJoin(Users, on = Users.id eq Notes.userId)
|
||||||
|
.select(Notes.id, Notes.title, Notes.updatedAt)
|
||||||
|
.where { Users.email eq email }
|
||||||
|
.orderBy(Notes.updatedAt.desc())
|
||||||
|
.map { row ->
|
||||||
|
Notes.createEntity(row)
|
||||||
|
}
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
val response = mutableListOf<ResponseItem>()
|
||||||
|
|
||||||
|
list.forEach { note ->
|
||||||
|
val tags = db.from(Tags)
|
||||||
|
.select(Tags.name)
|
||||||
|
.where { Tags.noteId eq note.id }
|
||||||
|
.map { it[Tags.name]!! }
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
val updatedAt = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(note.updatedAt)
|
||||||
|
|
||||||
|
val item = ResponseItem(
|
||||||
|
title = note.title,
|
||||||
|
tags = tags,
|
||||||
|
updatedAt = updatedAt
|
||||||
|
)
|
||||||
|
response += item
|
||||||
|
}
|
||||||
|
|
||||||
|
call.respond(response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -26,6 +26,8 @@ class UserController(kodein: Kodein) : KodeinController(kodein) {
|
|||||||
private val db by instance<Database>()
|
private val db by instance<Database>()
|
||||||
|
|
||||||
override fun Routing.registerRoutes() {
|
override fun Routing.registerRoutes() {
|
||||||
|
|
||||||
|
|
||||||
post<Routes.Login> {
|
post<Routes.Login> {
|
||||||
data class Response(val token: String)
|
data class Response(val token: String)
|
||||||
|
|
||||||
|
|||||||
@ -9,5 +9,5 @@ interface Note : Entity<Note> {
|
|||||||
val id: Int
|
val id: Int
|
||||||
var title: String
|
var title: String
|
||||||
var user: User
|
var user: User
|
||||||
var lastViewed: LocalDateTime?
|
var updatedAt: LocalDateTime
|
||||||
}
|
}
|
||||||
@ -12,4 +12,5 @@ object Chapters : Table<Chapter>("Chapters") {
|
|||||||
val number by int("number").bindTo { it.number }
|
val number by int("number").bindTo { it.number }
|
||||||
val content by text("content").bindTo { it.content }
|
val content by text("content").bindTo { it.content }
|
||||||
val noteId by int("note_id").references(Notes) { it.note }
|
val noteId by int("note_id").references(Notes) { it.note }
|
||||||
|
val note get() = noteId.referenceTable as Notes
|
||||||
}
|
}
|
||||||
@ -6,6 +6,7 @@ import me.liuwj.ktorm.schema.*
|
|||||||
object Notes : Table<Note>("Notes") {
|
object Notes : Table<Note>("Notes") {
|
||||||
val id by int("id").primaryKey().bindTo { it.id }
|
val id by int("id").primaryKey().bindTo { it.id }
|
||||||
val title by varchar("title").bindTo { it.title }
|
val title by varchar("title").bindTo { it.title }
|
||||||
val user by int("user_id").references(Users) { it.user }
|
val userId by int("user_id").references(Users) { it.user }
|
||||||
val lastViewed by datetime("last_viewed").bindTo { it.lastViewed }
|
val updatedAt by datetime("updated_at").bindTo { it.updatedAt }
|
||||||
|
val user get() = userId.referenceTable as Users
|
||||||
}
|
}
|
||||||
@ -9,4 +9,5 @@ object Tags : Table<Tag>("Tags") {
|
|||||||
val id by int("id").primaryKey().bindTo { it.id }
|
val id by int("id").primaryKey().bindTo { it.id }
|
||||||
val name by varchar("name").bindTo { it.name }
|
val name by varchar("name").bindTo { it.name }
|
||||||
val noteId by int("note_id").references(Notes) { it.note }
|
val noteId by int("note_id").references(Notes) { it.note }
|
||||||
|
val note get() = noteId.referenceTable as Notes
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user