Better seperation
This commit is contained in:
parent
3306b16f91
commit
5b748e1c10
@ -1,77 +0,0 @@
|
|||||||
# Register a new user
|
|
||||||
POST http://localhost:8081/user/login
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{
|
|
||||||
"username": "{{username}}",
|
|
||||||
"password": "{{password}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
> {%
|
|
||||||
client.global.set("token", response.body.token);
|
|
||||||
client.global.set("refreshToken", response.body.refreshToken);
|
|
||||||
client.test("Request executed successfully", function() {
|
|
||||||
client.assert(response.status === 200, "Response status is not 200");
|
|
||||||
});
|
|
||||||
%}
|
|
||||||
|
|
||||||
### Refresh token
|
|
||||||
POST http://localhost:8081/user/refresh_token
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{
|
|
||||||
"refreshToken": "{{refreshToken}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
> {%
|
|
||||||
client.test("Request executed successfully", function() {
|
|
||||||
client.global.set("token", response.body.token);
|
|
||||||
client.assert(response.status === 200, "Response status is not 200");
|
|
||||||
});
|
|
||||||
%}
|
|
||||||
|
|
||||||
### Get notes
|
|
||||||
GET http://localhost:8081/notes
|
|
||||||
Authorization: Bearer {{token}}
|
|
||||||
|
|
||||||
> {%
|
|
||||||
client.global.set("uuid", response.body[0].uuid);
|
|
||||||
client.test("Request executed successfully", function() {
|
|
||||||
client.assert(response.status === 200, "Response status is not 200");
|
|
||||||
});
|
|
||||||
%}
|
|
||||||
|
|
||||||
### Create note
|
|
||||||
POST http://localhost:8081/notes
|
|
||||||
Authorization: Bearer {{token}}
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{
|
|
||||||
"title": "test",
|
|
||||||
"tags": [
|
|
||||||
"Some",
|
|
||||||
"Tags"
|
|
||||||
],
|
|
||||||
"chapters": [
|
|
||||||
{
|
|
||||||
"title": "Chapter 1",
|
|
||||||
"content": "# This is some content"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
> {%
|
|
||||||
client.test("Request executed successfully", function() {
|
|
||||||
client.assert(response.status === 201, "Response status is not 201");
|
|
||||||
});
|
|
||||||
%}
|
|
||||||
|
|
||||||
### Get a note
|
|
||||||
GET http://localhost:8081/notes/{{uuid}}
|
|
||||||
Authorization: Bearer {{token}}
|
|
||||||
|
|
||||||
> {%
|
|
||||||
client.test("Request executed successfully", function() {
|
|
||||||
client.assert(response.status === 200, "Response status is not 200");
|
|
||||||
});
|
|
||||||
%}
|
|
||||||
@ -7,7 +7,7 @@ database:
|
|||||||
|
|
||||||
server:
|
server:
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
port: 8081
|
port: ${PORT:-8081}
|
||||||
cors: true
|
cors: true
|
||||||
|
|
||||||
jwt:
|
jwt:
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<withJansi>true</withJansi>
|
||||||
<encoder>
|
<encoder>
|
||||||
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
<pattern>%cyan(%d{YYYY-MM-dd HH:mm:ss.SSS}) [%thread] %highlight(%-5level) %magenta(%logger{36}) - %msg%n
|
||||||
|
</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
<root level="DEBUG">
|
<root level="INFO">
|
||||||
<appender-ref ref="STDOUT"/>
|
<appender-ref ref="STDOUT"/>
|
||||||
</root>
|
</root>
|
||||||
<logger name="me.liuwj.ktorm.database" level="INFO"/>
|
<logger name="me.liuwj.ktorm.database" level="INFO"/>
|
||||||
|
|||||||
@ -3,11 +3,16 @@ package be.vandewalleh
|
|||||||
import be.vandewalleh.features.Config
|
import be.vandewalleh.features.Config
|
||||||
import be.vandewalleh.features.loadFeatures
|
import be.vandewalleh.features.loadFeatures
|
||||||
import be.vandewalleh.migrations.Migration
|
import be.vandewalleh.migrations.Migration
|
||||||
import be.vandewalleh.routing.registerRoutes
|
import be.vandewalleh.routing.noteRoutes
|
||||||
|
import be.vandewalleh.routing.tagsRoute
|
||||||
|
import be.vandewalleh.routing.userRoutes
|
||||||
|
import com.sksamuel.hoplite.fp.valid
|
||||||
import io.ktor.application.*
|
import io.ktor.application.*
|
||||||
|
import io.ktor.auth.*
|
||||||
import io.ktor.routing.*
|
import io.ktor.routing.*
|
||||||
import io.ktor.server.engine.*
|
import io.ktor.server.engine.*
|
||||||
import io.ktor.server.netty.*
|
import io.ktor.server.netty.*
|
||||||
|
import me.liuwj.ktorm.database.*
|
||||||
import org.kodein.di.Kodein
|
import org.kodein.di.Kodein
|
||||||
import org.kodein.di.description
|
import org.kodein.di.description
|
||||||
import org.kodein.di.generic.instance
|
import org.kodein.di.generic.instance
|
||||||
@ -50,7 +55,17 @@ fun Application.module(kodein: Kodein) {
|
|||||||
loadFeatures(kodein)
|
loadFeatures(kodein)
|
||||||
|
|
||||||
routing {
|
routing {
|
||||||
registerRoutes(kodein)
|
route("/user") {
|
||||||
|
userRoutes(kodein)
|
||||||
|
}
|
||||||
|
authenticate {
|
||||||
|
route("/notes") {
|
||||||
|
noteRoutes(kodein)
|
||||||
|
}
|
||||||
|
route("/tags") {
|
||||||
|
tagsRoute(kodein)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,10 +7,10 @@ import io.ktor.http.*
|
|||||||
import io.ktor.response.*
|
import io.ktor.response.*
|
||||||
|
|
||||||
suspend fun ApplicationCall.respondStatus(status: HttpStatusCode) {
|
suspend fun ApplicationCall.respondStatus(status: HttpStatusCode) {
|
||||||
respond(status, """{"msg": "${status.description}"}""")
|
respond(status, """{"status": "${status.description}"}""")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the userId for the currently authenticated user
|
* @return the userId for the currently authenticated user
|
||||||
*/
|
*/
|
||||||
fun ApplicationCall.userId() = principal<UserDbIdPrincipal>()!!.id
|
fun ApplicationCall.authenticatedUserId() = principal<UserDbIdPrincipal>()!!.id
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
package be.vandewalleh.extensions
|
|
||||||
|
|
||||||
import io.ktor.http.*
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
fun Parameters.noteUuid(): UUID {
|
|
||||||
return UUID.fromString(this["noteUuid"])
|
|
||||||
}
|
|
||||||
@ -60,6 +60,7 @@ private fun configureDatasource(kodein: Kodein): DataSource {
|
|||||||
jdbcUrl = "jdbc:mariadb://$host:$port/$name"
|
jdbcUrl = "jdbc:mariadb://$host:$port/$name"
|
||||||
username = dbConfig.username
|
username = dbConfig.username
|
||||||
password = dbConfig.password.value
|
password = dbConfig.password.value
|
||||||
|
connectionTimeout = 3000 // 3 seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
return HikariDataSource(hikariConfig)
|
return HikariDataSource(hikariConfig)
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
package be.vandewalleh.features
|
package be.vandewalleh.features
|
||||||
|
|
||||||
import am.ik.yavi.core.ViolationDetail
|
|
||||||
import be.vandewalleh.validation.ValidationException
|
|
||||||
import io.ktor.application.*
|
import io.ktor.application.*
|
||||||
import io.ktor.features.*
|
import io.ktor.features.*
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.response.*
|
import io.ktor.response.*
|
||||||
import io.ktor.utils.io.errors.*
|
import io.ktor.utils.io.errors.*
|
||||||
|
import java.sql.SQLTransientConnectionException
|
||||||
|
|
||||||
fun Application.handleErrors() {
|
fun Application.handleErrors() {
|
||||||
install(StatusPages) {
|
install(StatusPages) {
|
||||||
@ -17,12 +16,15 @@ fun Application.handleErrors() {
|
|||||||
call.respond(HttpStatusCode.BadRequest)
|
call.respond(HttpStatusCode.BadRequest)
|
||||||
}
|
}
|
||||||
exception<ValidationException> {
|
exception<ValidationException> {
|
||||||
val error = ViolationError(it.details[0])
|
call.respond(HttpStatusCode.BadRequest, ErrorResponse(it.error))
|
||||||
call.respond(HttpStatusCode.BadRequest, error)
|
}
|
||||||
|
|
||||||
|
exception<SQLTransientConnectionException> {
|
||||||
|
val error = mapOf("error" to "It seems the server can't connect to the database")
|
||||||
|
call.respond(HttpStatusCode.InternalServerError, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViolationError(detail: ViolationDetail) {
|
class ValidationException(val error: String) : RuntimeException()
|
||||||
val msg = detail.defaultMessage
|
class ErrorResponse(val error: String)
|
||||||
}
|
|
||||||
|
|||||||
75
api/src/routing/NoteRoutes.kt
Normal file
75
api/src/routing/NoteRoutes.kt
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package be.vandewalleh.routing
|
||||||
|
|
||||||
|
import be.vandewalleh.extensions.authenticatedUserId
|
||||||
|
import be.vandewalleh.extensions.respondStatus
|
||||||
|
import be.vandewalleh.features.ValidationException
|
||||||
|
import be.vandewalleh.services.NoteService
|
||||||
|
import be.vandewalleh.validation.noteValidator
|
||||||
|
import be.vandewalleh.validation.receiveValidated
|
||||||
|
import io.ktor.application.*
|
||||||
|
import io.ktor.http.*
|
||||||
|
import io.ktor.response.*
|
||||||
|
import io.ktor.routing.*
|
||||||
|
import org.kodein.di.Kodein
|
||||||
|
import org.kodein.di.generic.instance
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
fun Route.noteRoutes(kodein: Kodein) {
|
||||||
|
val noteService by kodein.instance<NoteService>()
|
||||||
|
|
||||||
|
post {
|
||||||
|
val userId = call.authenticatedUserId()
|
||||||
|
val note = call.receiveValidated(noteValidator)
|
||||||
|
val createdNote = noteService.create(userId, note)
|
||||||
|
call.respond(HttpStatusCode.Created, createdNote)
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
val userId = call.authenticatedUserId()
|
||||||
|
val notes = noteService.findAll(userId)
|
||||||
|
call.respond(notes)
|
||||||
|
}
|
||||||
|
|
||||||
|
route("/{uuid}") {
|
||||||
|
|
||||||
|
fun ApplicationCall.userIdNoteIdPair(): Pair<Int, UUID> {
|
||||||
|
val userId = authenticatedUserId()
|
||||||
|
val uuid = parameters["uuid"]
|
||||||
|
val noteUuid = try {
|
||||||
|
UUID.fromString(uuid)
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
throw ValidationException("`$uuid` is not a valid UUID")
|
||||||
|
}
|
||||||
|
return userId to noteUuid
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
val (userId, noteUuid) = call.userIdNoteIdPair()
|
||||||
|
|
||||||
|
val response = noteService.find(userId, noteUuid)
|
||||||
|
?: return@get call.respondStatus(HttpStatusCode.NotFound)
|
||||||
|
call.respond(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
put {
|
||||||
|
val (userId, noteUuid) = call.userIdNoteIdPair()
|
||||||
|
|
||||||
|
val note = call.receiveValidated(noteValidator)
|
||||||
|
note.uuid = noteUuid
|
||||||
|
|
||||||
|
if (noteService.updateNote(userId, note))
|
||||||
|
call.respondStatus(HttpStatusCode.OK)
|
||||||
|
else call.respondStatus(HttpStatusCode.NotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete {
|
||||||
|
val (userId, noteUuid) = call.userIdNoteIdPair()
|
||||||
|
|
||||||
|
if (noteService.delete(userId, noteUuid))
|
||||||
|
call.respondStatus(HttpStatusCode.OK)
|
||||||
|
else
|
||||||
|
call.respondStatus(HttpStatusCode.NotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,17 +0,0 @@
|
|||||||
package be.vandewalleh.routing
|
|
||||||
|
|
||||||
import be.vandewalleh.routing.notes.notes
|
|
||||||
import be.vandewalleh.routing.notes.tags
|
|
||||||
import be.vandewalleh.routing.notes.title
|
|
||||||
import be.vandewalleh.routing.user.auth
|
|
||||||
import be.vandewalleh.routing.user.user
|
|
||||||
import io.ktor.routing.*
|
|
||||||
import org.kodein.di.Kodein
|
|
||||||
|
|
||||||
fun Routing.registerRoutes(kodein: Kodein) {
|
|
||||||
user(kodein)
|
|
||||||
auth(kodein)
|
|
||||||
notes(kodein)
|
|
||||||
title(kodein)
|
|
||||||
tags(kodein)
|
|
||||||
}
|
|
||||||
@ -1,20 +1,17 @@
|
|||||||
package be.vandewalleh.routing.notes
|
package be.vandewalleh.routing
|
||||||
|
|
||||||
import be.vandewalleh.extensions.userId
|
import be.vandewalleh.extensions.authenticatedUserId
|
||||||
import be.vandewalleh.services.NoteService
|
import be.vandewalleh.services.NoteService
|
||||||
import io.ktor.application.*
|
import io.ktor.application.*
|
||||||
import io.ktor.auth.*
|
|
||||||
import io.ktor.response.*
|
import io.ktor.response.*
|
||||||
import io.ktor.routing.*
|
import io.ktor.routing.*
|
||||||
import org.kodein.di.Kodein
|
import org.kodein.di.Kodein
|
||||||
import org.kodein.di.generic.instance
|
import org.kodein.di.generic.instance
|
||||||
|
|
||||||
fun Routing.tags(kodein: Kodein) {
|
fun Route.tagsRoute(kodein: Kodein) {
|
||||||
val noteService by kodein.instance<NoteService>()
|
val noteService by kodein.instance<NoteService>()
|
||||||
|
|
||||||
authenticate {
|
get {
|
||||||
get("/tags") {
|
call.respond(noteService.getTags(call.authenticatedUserId()))
|
||||||
call.respond(noteService.getTags(call.userId()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,11 +1,14 @@
|
|||||||
package be.vandewalleh.routing.user
|
package be.vandewalleh.routing
|
||||||
|
|
||||||
import be.vandewalleh.auth.SimpleJWT
|
import be.vandewalleh.auth.SimpleJWT
|
||||||
import be.vandewalleh.auth.UserDbIdPrincipal
|
import be.vandewalleh.auth.UserDbIdPrincipal
|
||||||
import be.vandewalleh.auth.UsernamePasswordCredential
|
import be.vandewalleh.auth.UsernamePasswordCredential
|
||||||
import be.vandewalleh.extensions.respondStatus
|
import be.vandewalleh.extensions.respondStatus
|
||||||
|
import be.vandewalleh.extensions.authenticatedUserId
|
||||||
import be.vandewalleh.features.PasswordHash
|
import be.vandewalleh.features.PasswordHash
|
||||||
import be.vandewalleh.services.UserService
|
import be.vandewalleh.services.UserService
|
||||||
|
import be.vandewalleh.validation.receiveValidated
|
||||||
|
import be.vandewalleh.validation.registerValidator
|
||||||
import com.auth0.jwt.exceptions.JWTVerificationException
|
import com.auth0.jwt.exceptions.JWTVerificationException
|
||||||
import io.ktor.application.*
|
import io.ktor.application.*
|
||||||
import io.ktor.auth.*
|
import io.ktor.auth.*
|
||||||
@ -19,13 +22,25 @@ import org.kodein.di.generic.instance
|
|||||||
data class RefreshToken(val refreshToken: String)
|
data class RefreshToken(val refreshToken: String)
|
||||||
data class DualToken(val token: String, val refreshToken: String)
|
data class DualToken(val token: String, val refreshToken: String)
|
||||||
|
|
||||||
fun Routing.auth(kodein: Kodein) {
|
fun Route.userRoutes(kodein: Kodein) {
|
||||||
val authSimpleJwt by kodein.instance<SimpleJWT>("auth")
|
val authSimpleJwt by kodein.instance<SimpleJWT>("auth")
|
||||||
val refreshSimpleJwt by kodein.instance<SimpleJWT>("refresh")
|
val refreshSimpleJwt by kodein.instance<SimpleJWT>("refresh")
|
||||||
val userService by kodein.instance<UserService>()
|
val userService by kodein.instance<UserService>()
|
||||||
val passwordHash by kodein.instance<PasswordHash>()
|
val passwordHash by kodein.instance<PasswordHash>()
|
||||||
|
|
||||||
post("/user/login") {
|
post {
|
||||||
|
val user = call.receiveValidated(registerValidator)
|
||||||
|
|
||||||
|
if (userService.exists(user.username))
|
||||||
|
return@post call.respondStatus(HttpStatusCode.Conflict)
|
||||||
|
|
||||||
|
val newUser = userService.create(user.username, user.password)
|
||||||
|
?: return@post call.respondStatus(HttpStatusCode.Conflict)
|
||||||
|
|
||||||
|
call.respond(HttpStatusCode.Created, newUser)
|
||||||
|
}
|
||||||
|
|
||||||
|
post("/login") {
|
||||||
val credential = call.receive<UsernamePasswordCredential>()
|
val credential = call.receive<UsernamePasswordCredential>()
|
||||||
|
|
||||||
val user = userService.find(credential.username)
|
val user = userService.find(credential.username)
|
||||||
@ -42,7 +57,7 @@ fun Routing.auth(kodein: Kodein) {
|
|||||||
return@post call.respond(response)
|
return@post call.respond(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
post("/user/refresh_token") {
|
post("/refresh_token") {
|
||||||
val token = call.receive<RefreshToken>().refreshToken
|
val token = call.receive<RefreshToken>().refreshToken
|
||||||
|
|
||||||
val id = try {
|
val id = try {
|
||||||
@ -63,12 +78,19 @@ fun Routing.auth(kodein: Kodein) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
authenticate {
|
authenticate {
|
||||||
get("/user/me") {
|
delete {
|
||||||
|
val userId = call.authenticatedUserId()
|
||||||
|
call.respondStatus(
|
||||||
|
if (userService.delete(userId)) HttpStatusCode.OK
|
||||||
|
else HttpStatusCode.NotFound
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
get("/me") {
|
||||||
val id = call.principal<UserDbIdPrincipal>()!!.id
|
val id = call.principal<UserDbIdPrincipal>()!!.id
|
||||||
val info = userService.find(id)
|
val info = userService.find(id)
|
||||||
if (info != null) call.respond(mapOf("user" to info))
|
if (info != null) call.respond(mapOf("user" to info))
|
||||||
else call.respondStatus(HttpStatusCode.Unauthorized)
|
else call.respondStatus(HttpStatusCode.Unauthorized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1,53 +0,0 @@
|
|||||||
package be.vandewalleh.routing.notes
|
|
||||||
|
|
||||||
import be.vandewalleh.entities.Note
|
|
||||||
import be.vandewalleh.extensions.noteUuid
|
|
||||||
import be.vandewalleh.extensions.respondStatus
|
|
||||||
import be.vandewalleh.extensions.userId
|
|
||||||
import be.vandewalleh.services.NoteService
|
|
||||||
import io.ktor.application.call
|
|
||||||
import io.ktor.auth.authenticate
|
|
||||||
import io.ktor.http.HttpStatusCode
|
|
||||||
import io.ktor.request.*
|
|
||||||
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 noteService by kodein.instance<NoteService>()
|
|
||||||
|
|
||||||
authenticate {
|
|
||||||
route("/notes/{noteUuid}") {
|
|
||||||
get {
|
|
||||||
val userId = call.userId()
|
|
||||||
val noteUuid = call.parameters.noteUuid()
|
|
||||||
|
|
||||||
val response = noteService.find(userId, noteUuid)
|
|
||||||
?: return@get call.respondStatus(HttpStatusCode.NotFound)
|
|
||||||
call.respond(response)
|
|
||||||
}
|
|
||||||
|
|
||||||
put {
|
|
||||||
val userId = call.userId()
|
|
||||||
val noteUuid = call.parameters.noteUuid()
|
|
||||||
val note = call.receive<Note>()
|
|
||||||
note.uuid = noteUuid
|
|
||||||
|
|
||||||
if (noteService.updateNote(userId, note))
|
|
||||||
call.respondStatus(HttpStatusCode.OK)
|
|
||||||
else call.respondStatus(HttpStatusCode.NotFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
delete {
|
|
||||||
val userId = call.userId()
|
|
||||||
val noteUuid = call.parameters.noteUuid()
|
|
||||||
|
|
||||||
if (noteService.delete(userId, noteUuid))
|
|
||||||
call.respondStatus(HttpStatusCode.OK)
|
|
||||||
else
|
|
||||||
call.respondStatus(HttpStatusCode.NotFound)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
package be.vandewalleh.routing.notes
|
|
||||||
|
|
||||||
import be.vandewalleh.entities.Note
|
|
||||||
import be.vandewalleh.extensions.userId
|
|
||||||
import be.vandewalleh.services.NoteService
|
|
||||||
import io.ktor.application.*
|
|
||||||
import io.ktor.auth.*
|
|
||||||
import io.ktor.http.*
|
|
||||||
import io.ktor.request.*
|
|
||||||
import io.ktor.response.*
|
|
||||||
import io.ktor.routing.*
|
|
||||||
import org.kodein.di.Kodein
|
|
||||||
import org.kodein.di.generic.instance
|
|
||||||
|
|
||||||
fun Routing.notes(kodein: Kodein) {
|
|
||||||
val noteService by kodein.instance<NoteService>()
|
|
||||||
|
|
||||||
authenticate {
|
|
||||||
get("/notes") {
|
|
||||||
val userId = call.userId()
|
|
||||||
val notes = noteService.findAll(userId)
|
|
||||||
call.respond(notes)
|
|
||||||
}
|
|
||||||
|
|
||||||
post("/notes") {
|
|
||||||
val userId = call.userId()
|
|
||||||
val note = call.receive<Note>()
|
|
||||||
val createdNote = noteService.create(userId, note)
|
|
||||||
call.respond(HttpStatusCode.Created, createdNote)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
package be.vandewalleh.routing.user
|
|
||||||
|
|
||||||
import be.vandewalleh.extensions.respondStatus
|
|
||||||
import be.vandewalleh.extensions.userId
|
|
||||||
import be.vandewalleh.services.UserService
|
|
||||||
import be.vandewalleh.validation.receiveValidated
|
|
||||||
import be.vandewalleh.validation.user.registerValidator
|
|
||||||
import io.ktor.application.*
|
|
||||||
import io.ktor.auth.*
|
|
||||||
import io.ktor.http.*
|
|
||||||
import io.ktor.response.*
|
|
||||||
import io.ktor.routing.*
|
|
||||||
import org.kodein.di.Kodein
|
|
||||||
import org.kodein.di.generic.instance
|
|
||||||
|
|
||||||
fun Routing.user(kodein: Kodein) {
|
|
||||||
val userService by kodein.instance<UserService>()
|
|
||||||
|
|
||||||
route("/user") {
|
|
||||||
post {
|
|
||||||
val user = call.receiveValidated(registerValidator)
|
|
||||||
|
|
||||||
if (userService.exists(user.username))
|
|
||||||
return@post call.respondStatus(HttpStatusCode.Conflict)
|
|
||||||
|
|
||||||
val newUser = userService.create(user.username, user.password)
|
|
||||||
?: return@post call.respondStatus(HttpStatusCode.Conflict)
|
|
||||||
|
|
||||||
call.respond(HttpStatusCode.Created, newUser)
|
|
||||||
}
|
|
||||||
|
|
||||||
authenticate {
|
|
||||||
delete {
|
|
||||||
val status = if (userService.delete(call.userId()))
|
|
||||||
HttpStatusCode.OK
|
|
||||||
else
|
|
||||||
HttpStatusCode.NotFound
|
|
||||||
call.respondStatus(status)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -26,7 +26,7 @@ class NoteService(override val kodein: Kodein) : KodeinAware {
|
|||||||
suspend fun findAll(userId: Int): List<Note> {
|
suspend fun findAll(userId: Int): List<Note> {
|
||||||
val notes = launchIo {
|
val notes = launchIo {
|
||||||
db.sequenceOf(Notes, withReferences = false)
|
db.sequenceOf(Notes, withReferences = false)
|
||||||
.filterColumns { it.columns - it.userId - it.content }
|
.filterColumns { listOf(it.uuid, it.title, it.updatedAt) }
|
||||||
.filter { it.userId eq userId }
|
.filter { it.userId eq userId }
|
||||||
.sortedByDescending { it.updatedAt }
|
.sortedByDescending { it.updatedAt }
|
||||||
.toList()
|
.toList()
|
||||||
@ -41,7 +41,7 @@ class NoteService(override val kodein: Kodein) : KodeinAware {
|
|||||||
.toList()
|
.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
val tagsByUuid = allTags.groupBy({ it.note.uuid }, { it.name })
|
val tagsByUuid = allTags.groupByTo(HashMap(), { it.note.uuid }, { it.name })
|
||||||
|
|
||||||
notes.forEach {
|
notes.forEach {
|
||||||
val tags = tagsByUuid[it.uuid]
|
val tags = tagsByUuid[it.uuid]
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package be.vandewalleh.validation.user
|
package be.vandewalleh.validation
|
||||||
|
|
||||||
import am.ik.yavi.builder.ValidatorBuilder
|
import am.ik.yavi.builder.ValidatorBuilder
|
||||||
import am.ik.yavi.builder.konstraint
|
import am.ik.yavi.builder.konstraint
|
||||||
@ -1,14 +1,12 @@
|
|||||||
package be.vandewalleh.validation
|
package be.vandewalleh.validation
|
||||||
|
|
||||||
import am.ik.yavi.core.Validator
|
import am.ik.yavi.core.Validator
|
||||||
import am.ik.yavi.core.ViolationDetail
|
import be.vandewalleh.features.ValidationException
|
||||||
import io.ktor.application.*
|
import io.ktor.application.*
|
||||||
import io.ktor.request.*
|
import io.ktor.request.*
|
||||||
|
|
||||||
suspend inline fun <reified T : Any> ApplicationCall.receiveValidated(validator: Validator<T>): T {
|
suspend inline fun <reified T : Any> ApplicationCall.receiveValidated(validator: Validator<T>): T {
|
||||||
val value: T = receive()
|
val value: T = receive()
|
||||||
validator.validate(value).throwIfInvalid { ValidationException(it.details()) }
|
validator.validate(value).throwIfInvalid { ValidationException(it.details()[0].defaultMessage) }
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ValidationException(val details: List<ViolationDetail>) : RuntimeException()
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
package unit.validation
|
package unit.validation
|
||||||
|
|
||||||
import be.vandewalleh.entities.User
|
import be.vandewalleh.entities.User
|
||||||
import be.vandewalleh.validation.user.registerValidator
|
import be.vandewalleh.validation.registerValidator
|
||||||
import org.amshove.kluent.*
|
import org.amshove.kluent.*
|
||||||
import org.junit.jupiter.api.*
|
import org.junit.jupiter.api.*
|
||||||
import utils.firstInvalid
|
import utils.firstInvalid
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user