diff --git a/api-doc/users/index.apib b/api-doc/users/index.apib index 126ce93..e4fd27c 100644 --- a/api-doc/users/index.apib +++ b/api-doc/users/index.apib @@ -34,7 +34,7 @@ ## Authenticate user [/login] -Authenticate one user to access protected routes. +Authenticate one user to access protected routing. ### Authenticate a user [POST] diff --git a/api/src/NotesApplication.kt b/api/src/NotesApplication.kt index c9d1d57..c1c3cff 100644 --- a/api/src/NotesApplication.kt +++ b/api/src/NotesApplication.kt @@ -1,14 +1,20 @@ package be.vandewalleh -import be.vandewalleh.controllers.base.KodeinController -import be.vandewalleh.controllers.controllerModule import be.vandewalleh.features.configurationFeature import be.vandewalleh.features.configurationModule import be.vandewalleh.features.features import be.vandewalleh.migrations.Migration +import be.vandewalleh.routing.registerRoutes import be.vandewalleh.services.serviceModule import io.ktor.application.Application +import io.ktor.application.call +import io.ktor.application.feature import io.ktor.application.log +import io.ktor.response.respond +import io.ktor.routing.Route +import io.ktor.routing.Routing +import io.ktor.routing.RoutingPath.Companion.root +import io.ktor.routing.get import io.ktor.routing.routing import me.liuwj.ktorm.database.Database import org.kodein.di.Kodein @@ -26,7 +32,6 @@ fun Application.module() { configurationFeature() kodein = Kodein { - import(controllerModule) import(configurationModule) import(serviceModule) @@ -41,11 +46,18 @@ fun Application.module() { val migration by kodein.instance() migration.migrate() - val controllers by kodein.instance>() - routing { - controllers.forEach { - it.apply { registerRoutes() } - } + registerRoutes(kodein) } + + val root = feature(Routing) + val allRoutes = allRoutes(root) + allRoutes.forEach { + println(it.toString()) + } + +} + +fun allRoutes(root: Route): List { + return listOf(root) + root.children.flatMap { allRoutes(it) } } \ No newline at end of file diff --git a/api/src/auth/AuthenticationModule.kt b/api/src/auth/AuthenticationModule.kt index 1a9de0f..f57ad63 100644 --- a/api/src/auth/AuthenticationModule.kt +++ b/api/src/auth/AuthenticationModule.kt @@ -11,7 +11,7 @@ import org.kodein.di.generic.instance fun Application.authenticationModule() { install(Authentication) { jwt { - val simpleJwt: SimpleJWT by kodein.instance() + val simpleJwt by kodein.instance() verifier(simpleJwt.verifier) validate { UserIdPrincipal(it.payload.getClaim("name").asString()) diff --git a/api/src/controllers/ChaptersController.kt b/api/src/controllers/ChaptersController.kt deleted file mode 100644 index c42f2eb..0000000 --- a/api/src/controllers/ChaptersController.kt +++ /dev/null @@ -1,24 +0,0 @@ -package be.vandewalleh.controllers - -import be.vandewalleh.controllers.base.AuthCrudController -import io.ktor.application.ApplicationCall -import io.ktor.routing.Routing -import me.liuwj.ktorm.database.Database -import org.kodein.di.Kodein -import org.kodein.di.generic.instance - -class ChaptersController(kodein: Kodein) : AuthCrudController("/notes/{noteTitle}/chapters/{chapterNumber}", kodein) { - private val db by kodein.instance() - - private fun ApplicationCall.noteTitle(): String? { - return this.parameters["noteTitle"]!! - } - - private fun ApplicationCall.chapterNumber(): Int? { - return this.parameters["chapterNumber"]?.toIntOrNull() - } - - override fun Routing.routes() { - TODO("Not yet implemented") - } -} diff --git a/api/src/controllers/Controllers.kt b/api/src/controllers/Controllers.kt deleted file mode 100644 index 0ed5c15..0000000 --- a/api/src/controllers/Controllers.kt +++ /dev/null @@ -1,20 +0,0 @@ -package be.vandewalleh.controllers - -import be.vandewalleh.controllers.base.KodeinController -import org.kodein.di.Kodein -import org.kodein.di.generic.bind -import org.kodein.di.generic.inSet -import org.kodein.di.generic.setBinding -import org.kodein.di.generic.singleton - -/** - * [Kodein] controller module containing the app controllers - */ -val controllerModule = Kodein.Module(name = "Controller") { - bind() from setBinding() - - bind().inSet() with singleton { RegisterController(this.kodein) } - bind().inSet() with singleton { LoginController(this.kodein) } - bind().inSet() with singleton { NotesController(this.kodein) } - bind().inSet() with singleton { TitleController(this.kodein) } -} \ No newline at end of file diff --git a/api/src/controllers/RegisterController.kt b/api/src/controllers/RegisterController.kt deleted file mode 100644 index 93aba9e..0000000 --- a/api/src/controllers/RegisterController.kt +++ /dev/null @@ -1,35 +0,0 @@ -package be.vandewalleh.controllers - -import be.vandewalleh.controllers.base.KodeinController -import be.vandewalleh.services.UserRegistrationDto -import be.vandewalleh.services.UserService -import io.ktor.application.call -import io.ktor.http.HttpStatusCode -import io.ktor.request.receive -import io.ktor.response.respond -import io.ktor.routing.Routing -import io.ktor.routing.post -import org.kodein.di.Kodein -import org.kodein.di.generic.instance -import org.mindrot.jbcrypt.BCrypt - -class RegisterController(kodein: Kodein) : KodeinController("/register", kodein) { - private val userService by instance() - - override fun Routing.routes() { - post { - val user = call.receive() - - if (userService.userExists(user.username, user.email)) - return@post call.respond(HttpStatusCode.Conflict) - - val hashedPassword = BCrypt.hashpw(user.password, BCrypt.gensalt()) - - userService.createUser( - UserRegistrationDto(user.username, user.email, hashedPassword) - ) - - return@post call.respondStatus(HttpStatusCode.Created) - } - } -} \ No newline at end of file diff --git a/api/src/controllers/TitleController.kt b/api/src/controllers/TitleController.kt deleted file mode 100644 index 0e7d432..0000000 --- a/api/src/controllers/TitleController.kt +++ /dev/null @@ -1,171 +0,0 @@ -package be.vandewalleh.controllers - -import be.vandewalleh.controllers.base.AuthCrudController -import be.vandewalleh.entities.Note -import be.vandewalleh.entities.Tag -import be.vandewalleh.entities.User -import be.vandewalleh.tables.Chapters -import be.vandewalleh.tables.Notes -import be.vandewalleh.tables.Tags -import be.vandewalleh.tables.Users -import io.ktor.application.ApplicationCall -import io.ktor.application.call -import io.ktor.http.HttpStatusCode -import io.ktor.request.receive -import io.ktor.response.respond -import io.ktor.routing.* -import me.liuwj.ktorm.database.Database -import me.liuwj.ktorm.dsl.* -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 -import java.time.LocalDateTime - -class TitleController(kodein: Kodein) : AuthCrudController("/notes/{noteTitle}", kodein) { - private val db by kodein.instance() - - private fun ApplicationCall.noteTitle(): String? { - return this.parameters["noteTitle"]!! - } - - private fun ApplicationCall.user(): User { - return db.sequenceOf(Users) - .find { it.email eq this.userEmail() } - ?: error("") - } - - /** - * Method that returns a [Notes] ID from it's title and the currently logged in user. - * returns null if none found - */ - private fun ApplicationCall.requestedNoteId(): Int? { - val user = user() - val title = noteTitle() ?: error("title missing") - - return db.from(Notes) - .select(Notes.id) - .where { Notes.userId eq user.id and (Notes.title eq title) } - .limit(0, 1) - .map { it[Notes.id]!! } - .firstOrNull() - } - - private class PostRequestBody(val tags: List) - - private class ChapterDto(val title: String, val content: String) - private class GetResponseBody(val tags: List, val chapters: List) - - private class PatchRequestBody(val title: String? = null, val tags: List? = null) - - override fun Routing.routes() { - post { - val title = call.noteTitle() ?: error("") - val tags = call.receive().tags - - val user = call.user() - - val exists = call.requestedNoteId() != null - - if (exists) { - return@post call.respondStatus(HttpStatusCode.Conflict) - } - - db.useTransaction { - val note = Note { - this.title = title - this.user = user - this.updatedAt = LocalDateTime.now() - } - - db.sequenceOf(Notes).add(note) - - tags.forEach { tagName -> - val tag = Tag { - this.note = note - this.name = tagName - } - - db.sequenceOf(Tags).add(tag) - } - } - - call.respondStatus(HttpStatusCode.Created) - } - - get { - val noteId = call.requestedNoteId() - ?: return@get call.respondStatus(HttpStatusCode.NotFound) - - val tags = db.from(Tags) - .select(Tags.name) - .where { Tags.noteId eq noteId } - .map { it[Tags.name]!! } - .toList() - - val chapters = db.from(Chapters) - .select(Chapters.title, Chapters.content) - .where { Chapters.noteId eq noteId } - .orderBy(Chapters.number.asc()) - .map { ChapterDto(it[Chapters.title]!!, it[Chapters.content]!!) } - .toList() - - val response = GetResponseBody(tags, chapters) - - call.respond(response) - } - - patch { - val requestedChanges = call.receive() - - // This means no changes have been requested.. - if (requestedChanges.tags == null && requestedChanges.title == null) { - return@patch call.respondStatus(HttpStatusCode.BadRequest) - } - - val noteId = call.requestedNoteId() - ?: return@patch call.respondStatus(HttpStatusCode.NotFound) - - db.useTransaction { - if (requestedChanges.title != null) { - db.update(Notes) { - it.title to requestedChanges.title - where { it.id eq noteId } - } - } - - if (requestedChanges.tags != null) { - // delete all tags - db.delete(Tags) { - it.noteId eq noteId - } - - // put new ones - requestedChanges.tags.forEach { tagName -> - db.insert(Tags) { - it.name to tagName - it.noteId to noteId - } - } - } - - } - - call.respondStatus(HttpStatusCode.OK) - } - - delete { - val noteId = call.requestedNoteId() - ?: return@delete call.respondStatus(HttpStatusCode.NotFound) - - db.useTransaction { - db.delete(Tags) { it.noteId eq noteId } - db.delete(Chapters) { it.noteId eq noteId } - db.delete(Notes) { it.id eq noteId } - } - - call.respondStatus(HttpStatusCode.OK) - } - } -} \ No newline at end of file diff --git a/api/src/controllers/base/AuthCrudController.kt b/api/src/controllers/base/AuthCrudController.kt deleted file mode 100644 index b778f88..0000000 --- a/api/src/controllers/base/AuthCrudController.kt +++ /dev/null @@ -1,27 +0,0 @@ -package be.vandewalleh.controllers.base - -import be.vandewalleh.services.UserService -import io.ktor.application.ApplicationCall -import io.ktor.auth.UserIdPrincipal -import io.ktor.auth.principal -import org.kodein.di.Kodein -import org.kodein.di.generic.instance - -abstract class AuthCrudController( - path: String, - override val kodein: Kodein -) : - KodeinController(path, kodein, auth = true) { - - private val userService by instance() - - /** - * retrieves the user email from the JWT token - */ - fun ApplicationCall.userEmail(): String = - this.principal()!!.name - - fun ApplicationCall.userId(): Int = - userService.getUserId(userEmail())!! - -} diff --git a/api/src/controllers/base/KodeinController.kt b/api/src/controllers/base/KodeinController.kt deleted file mode 100644 index c287205..0000000 --- a/api/src/controllers/base/KodeinController.kt +++ /dev/null @@ -1,41 +0,0 @@ -package be.vandewalleh.controllers.base - -import io.ktor.application.ApplicationCall -import io.ktor.auth.authenticate -import io.ktor.http.HttpStatusCode -import io.ktor.response.respond -import io.ktor.routing.Routing -import io.ktor.routing.route -import org.kodein.di.Kodein -import org.kodein.di.KodeinAware - -abstract class KodeinController( - private val path: String, - override val kodein: Kodein, - private val auth: Boolean = false -) : KodeinAware { - - /** - * Method that subtypes must override to declare their [Routing] routes. - */ - abstract fun Routing.routes() - - - fun Routing.registerRoutes() { - if (auth) { - authenticate { - route(path) { - this@registerRoutes.routes() - } - } - } else { - route(path) { - this@registerRoutes.routes() - } - } - } - - suspend fun ApplicationCall.respondStatus(status: HttpStatusCode) { - this.respond(status, mapOf("message" to status.description)) - } -} diff --git a/api/src/extensions/ApplicationCallExtensions.kt b/api/src/extensions/ApplicationCallExtensions.kt new file mode 100644 index 0000000..de8ee00 --- /dev/null +++ b/api/src/extensions/ApplicationCallExtensions.kt @@ -0,0 +1,35 @@ +package be.vandewalleh.extensions + +import be.vandewalleh.kodein +import be.vandewalleh.services.UserService +import io.ktor.application.ApplicationCall +import io.ktor.auth.UserIdPrincipal +import io.ktor.auth.principal +import io.ktor.http.HttpStatusCode +import io.ktor.request.receive +import io.ktor.response.respond +import org.kodein.di.generic.instance + +val userService by kodein.instance() + +suspend fun ApplicationCall.respondStatus(status: HttpStatusCode) { + respond(status, status.description) +} + +/** + * @return the userId for the currently authenticated user + */ +fun ApplicationCall.userId(): Int { + val email = principal()!!.name + return userService.getUserId(email)!! +} + +private class Tags(val tags: List) + +suspend fun ApplicationCall.receiveTags(): List { + return receive().tags +} + +data class NotePatch(val tags: List?, val title: String?) + +suspend fun ApplicationCall.receiveNotePatch() = receive() \ No newline at end of file diff --git a/api/src/extensions/ParametersExtensions.kt b/api/src/extensions/ParametersExtensions.kt new file mode 100644 index 0000000..9b0b208 --- /dev/null +++ b/api/src/extensions/ParametersExtensions.kt @@ -0,0 +1,22 @@ +package be.vandewalleh.extensions + +import be.vandewalleh.kodein +import be.vandewalleh.services.NotesService +import be.vandewalleh.tables.Notes +import io.ktor.http.Parameters +import org.kodein.di.generic.instance + +val notesService by kodein.instance() + +fun Parameters.noteTitle(): String { + return this["noteTitle"]!! +} + +/** + * Method that returns a [Notes] ID from it's title and the currently logged in user. + * returns null if none found + */ +fun Parameters.noteId(userId: Int): Int? { + val title = noteTitle() + return notesService.getNoteIdFromUserIdAndTitle(userId, title) +} \ No newline at end of file diff --git a/api/src/routing/ChaptersController.kt b/api/src/routing/ChaptersController.kt new file mode 100644 index 0000000..76850b7 --- /dev/null +++ b/api/src/routing/ChaptersController.kt @@ -0,0 +1,14 @@ +package be.vandewalleh.routing + +import io.ktor.auth.authenticate +import io.ktor.routing.Routing +import io.ktor.routing.route +import org.kodein.di.Kodein + +fun Routing.chapters(kodein: Kodein) { + authenticate { + route("/notes/{noteTitle}/chapters/{chapterNumber}") { + + } + } +} diff --git a/api/src/controllers/LoginController.kt b/api/src/routing/LoginController.kt similarity index 64% rename from api/src/controllers/LoginController.kt rename to api/src/routing/LoginController.kt index 176faf9..d776de1 100644 --- a/api/src/controllers/LoginController.kt +++ b/api/src/routing/LoginController.kt @@ -1,8 +1,7 @@ -package be.vandewalleh.controllers +package be.vandewalleh.routing import be.vandewalleh.auth.SimpleJWT import be.vandewalleh.auth.UsernamePasswordCredential -import be.vandewalleh.controllers.base.KodeinController import be.vandewalleh.services.UserService import io.ktor.application.call import io.ktor.http.HttpStatusCode @@ -10,30 +9,31 @@ import io.ktor.request.receive import io.ktor.response.respond import io.ktor.routing.Routing import io.ktor.routing.post +import io.ktor.routing.route import org.kodein.di.Kodein import org.kodein.di.generic.instance import org.mindrot.jbcrypt.BCrypt -class LoginController(kodein: Kodein) : KodeinController("/login", kodein) { - private val simpleJwt by instance() - private val userService by instance() +fun Routing.login(kodein: Kodein) { + val simpleJwt by kodein.instance() + val userService by kodein.instance() data class TokenResponse(val token: String) - - override fun Routing.routes() { - post { + route("/login"){ + post { val credential = call.receive() val (email, password) = userService.getEmailAndPasswordFromUsername(credential.username) - ?: return@post call.respondStatus(HttpStatusCode.Unauthorized) - + ?: return@post call.respond(HttpStatusCode.Unauthorized) if (!BCrypt.checkpw(credential.password, password)) { - return@post call.respondStatus(HttpStatusCode.Unauthorized) + return@post call.respond(HttpStatusCode.Unauthorized) } return@post call.respond(TokenResponse(simpleJwt.sign(email))) } } + + } \ No newline at end of file diff --git a/api/src/controllers/NotesController.kt b/api/src/routing/NotesController.kt similarity index 56% rename from api/src/controllers/NotesController.kt rename to api/src/routing/NotesController.kt index 5fdacb9..f4108ea 100644 --- a/api/src/controllers/NotesController.kt +++ b/api/src/routing/NotesController.kt @@ -1,19 +1,20 @@ -package be.vandewalleh.controllers +package be.vandewalleh.routing -import be.vandewalleh.controllers.base.AuthCrudController +import be.vandewalleh.extensions.userId import be.vandewalleh.services.NotesService import io.ktor.application.call +import io.ktor.auth.authenticate import io.ktor.response.respond import io.ktor.routing.Routing import io.ktor.routing.get import org.kodein.di.Kodein import org.kodein.di.generic.instance -class NotesController(kodein: Kodein) : AuthCrudController("/notes", kodein) { - private val notesService by kodein.instance() +fun Routing.notes(kodein: Kodein) { + val notesService by kodein.instance() - override fun Routing.routes() { - get { + authenticate { + get("/notes") { val userId = call.userId() val notes = notesService.getNotes(userId) call.respond(notes) diff --git a/api/src/routing/RegisterController.kt b/api/src/routing/RegisterController.kt new file mode 100644 index 0000000..8f32ae0 --- /dev/null +++ b/api/src/routing/RegisterController.kt @@ -0,0 +1,33 @@ +package be.vandewalleh.routing + +import be.vandewalleh.extensions.respondStatus +import be.vandewalleh.services.UserRegistrationDto +import be.vandewalleh.services.UserService +import io.ktor.application.call +import io.ktor.http.HttpStatusCode +import io.ktor.request.receive +import io.ktor.response.respond +import io.ktor.routing.Routing +import io.ktor.routing.post +import org.kodein.di.Kodein +import org.kodein.di.generic.instance +import org.mindrot.jbcrypt.BCrypt + +fun Routing.register(kodein: Kodein) { + val userService by kodein.instance() + + post("/register") { + val user = call.receive() + + if (userService.userExists(user.username, user.email)) + return@post call.respond(HttpStatusCode.Conflict) + + val hashedPassword = BCrypt.hashpw(user.password, BCrypt.gensalt()) + + userService.createUser( + UserRegistrationDto(user.username, user.email, hashedPassword) + ) + + return@post call.respondStatus(HttpStatusCode.Created) + } +} \ No newline at end of file diff --git a/api/src/routing/Routes.kt b/api/src/routing/Routes.kt new file mode 100644 index 0000000..31e6401 --- /dev/null +++ b/api/src/routing/Routes.kt @@ -0,0 +1,12 @@ +package be.vandewalleh.routing + +import io.ktor.routing.Routing +import org.kodein.di.Kodein + +fun Routing.registerRoutes(kodein: Kodein) { + login(kodein) + register(kodein) + notes(kodein) + title(kodein) + chapters(kodein) +} \ No newline at end of file diff --git a/api/src/routing/TitleController.kt b/api/src/routing/TitleController.kt new file mode 100644 index 0000000..4fb4e95 --- /dev/null +++ b/api/src/routing/TitleController.kt @@ -0,0 +1,64 @@ +package be.vandewalleh.routing + +import be.vandewalleh.extensions.* +import be.vandewalleh.services.NotesService +import io.ktor.application.call +import io.ktor.auth.authenticate +import io.ktor.http.HttpStatusCode +import io.ktor.response.respond +import io.ktor.routing.* +import org.kodein.di.Kodein +import org.kodein.di.generic.instance + +fun Routing.title(kodein: Kodein) { + val notesService by kodein.instance() + + authenticate { + route("/notes/{noteTitle}") { + post { + val userId = call.userId() + val title = call.parameters.noteTitle() + val tags = call.receiveTags() + val noteId = call.parameters.noteId(userId) + + if (noteId != null) { + return@post call.respondStatus(HttpStatusCode.Conflict) + } + + notesService.createNote(userId, title, tags) + call.respondStatus(HttpStatusCode.Created) + } + + get { + val userId = call.userId() + val noteId = call.parameters.noteId(userId) + ?: return@get call.respondStatus(HttpStatusCode.NotFound) + + val response = notesService.getTagsAndChapters(noteId) + call.respond(response) + } + + patch { + val notePatch = call.receiveNotePatch() + if (notePatch.tags == null && notePatch.title == null) + return@patch call.respondStatus(HttpStatusCode.BadRequest) + + val userId = call.userId() + val noteId = call.parameters.noteId(userId) + ?: return@patch call.respondStatus(HttpStatusCode.NotFound) + + notesService.updateNote(noteId, notePatch.tags, notePatch.title) + call.respondStatus(HttpStatusCode.OK) + } + + delete { + val userId = call.userId() + val noteId = call.parameters.noteId(userId) + ?: return@delete call.respondStatus(HttpStatusCode.NotFound) + + notesService.deleteNote(noteId) + call.respondStatus(HttpStatusCode.OK) + } + } + } +} diff --git a/api/src/services/NotesService.kt b/api/src/services/NotesService.kt index 43eed75..c88bef2 100644 --- a/api/src/services/NotesService.kt +++ b/api/src/services/NotesService.kt @@ -1,5 +1,6 @@ package be.vandewalleh.services +import be.vandewalleh.tables.Chapters import be.vandewalleh.tables.Notes import be.vandewalleh.tables.Tags import me.liuwj.ktorm.database.Database @@ -13,22 +14,20 @@ import java.time.format.DateTimeFormatter * service to handle database queries at the Notes level. */ class NotesService(override val kodein: Kodein) : KodeinAware { - val db by instance() + private val db by instance() /** * returns a list of [NotesDTO] associated with the userId */ - fun getNotes(userId: Int): List { - val notes = db.from(Notes) - .select(Notes.id, Notes.title, Notes.updatedAt) - .where { Notes.userId eq userId } - .orderBy(Notes.updatedAt.desc()) - .map { row -> - Notes.createEntity(row) - } - .toList() - - return notes.map { note -> + fun getNotes(userId: Int): List = db.from(Notes) + .select(Notes.id, Notes.title, Notes.updatedAt) + .where { Notes.userId eq userId } + .orderBy(Notes.updatedAt.desc()) + .map { row -> + Notes.createEntity(row) + } + .toList() + .map { note -> val tags = db.from(Tags) .select(Tags.name) .where { Tags.noteId eq note.id } @@ -40,8 +39,67 @@ class NotesService(override val kodein: Kodein) : KodeinAware { NotesDTO(note.title, tags, updatedAt) } + fun getNoteIdFromUserIdAndTitle(userId: Int, noteTitle: String): Int? = db.from(Notes) + .select(Notes.id) + .where { Notes.userId eq userId and (Notes.title eq noteTitle) } + .limit(0, 1) + .map { it[Notes.id]!! } + .firstOrNull() + + fun createNote(userId: Int, title: String, tags: List) { + TODO() } + fun getTagsAndChapters(noteId: Int): TagsChaptersDTO { + val tags = db.from(Tags) + .select(Tags.name) + .where { Tags.noteId eq noteId } + .map { it[Tags.name]!! } + .toList() + + val chapters = db.from(Chapters) + .select(Chapters.title, Chapters.content) + .where { Chapters.noteId eq noteId } + .orderBy(Chapters.number.asc()) + .map { ChaptersDTO(it[Chapters.title]!!, it[Chapters.content]!!) } + .toList() + + return TagsChaptersDTO(tags, chapters) + } + + fun updateNote(noteId: Int, tags: List?, title: String?): Unit = + db.useTransaction { + if (title != null) { + db.update(Notes) { + it.title to title + where { it.id eq noteId } + } + } + + if (tags != null) { + // delete all tags + db.delete(Tags) { + it.noteId eq noteId + } + + // put new ones + tags.forEach { tagName -> + db.insert(Tags) { + it.name to tagName + it.noteId to noteId + } + } + } + } + + fun deleteNote(noteId: Int): Unit = + db.useTransaction { + db.delete(Tags) { it.noteId eq noteId } + db.delete(Chapters) { it.noteId eq noteId } + db.delete(Notes) { it.id eq noteId } + } } +data class ChaptersDTO(val title: String, val content: String) +data class TagsChaptersDTO(val tags: List, val chapters: List) data class NotesDTO(val title: String, val tags: List, val updatedAt: String) \ No newline at end of file diff --git a/api/src/services/Services.kt b/api/src/services/Services.kt index fdb698c..6db6891 100644 --- a/api/src/services/Services.kt +++ b/api/src/services/Services.kt @@ -10,4 +10,5 @@ import org.kodein.di.generic.singleton */ val serviceModule = Kodein.Module(name = "Services") { bind() with singleton { NotesService(this.kodein) } + bind() with singleton { UserService(this.kodein) } } \ No newline at end of file diff --git a/public/index.html b/public/index.html index c840ea6..1e970fc 100644 --- a/public/index.html +++ b/public/index.html @@ -36,7 +36,8 @@ "type": "string" } } -}

Register a new user
POST/register


Authenticate user

Authenticate one user to access protected routes.

+}

Register a new user
POST/register


Authenticate user

Authenticate + one user to access protected routing.

POST http://localhost:5000/login
Requestsexample 1
Headers
Content-Type: application/json
Body
{
   "username": "babar",
   "password": "tortue"