Skip to content

Commit

Permalink
ids are string now: JavaScript can't handle the Java Long.
Browse files Browse the repository at this point in the history
  • Loading branch information
jvorhauer committed Feb 16, 2024
1 parent 129667e commit 01bbb7c
Show file tree
Hide file tree
Showing 18 changed files with 208 additions and 179 deletions.
2 changes: 1 addition & 1 deletion src/main/kotlin/blog/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import blog.read.Reader
import blog.read.info
import blog.write.Processor

const val pid: String = "27"
const val pid: String = "28"

object Main {
private val kfg: Konfig = ConfigFactory.load("application.conf").extract("konomas")
Expand Down
39 changes: 20 additions & 19 deletions src/main/kotlin/blog/model/Note.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import java.time.LocalDateTime
import java.time.ZoneId
import akka.actor.typed.Scheduler
import akka.actor.typed.javadsl.AskPattern
import io.hypersistence.tsid.TSID
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
Expand All @@ -18,55 +19,55 @@ import kotlinx.coroutines.future.await
import blog.read.Reader

data class CreateNoteRequest(val title: String, val body: String) {
fun toCommand(user: Long, replyTo: ActorRef<StatusReply<NoteResponse>>) = CreateNote(user, title, body, replyTo)
fun toCommand(user: String, replyTo: ActorRef<StatusReply<NoteResponse>>) = CreateNote(user, title, body, replyTo)
}

data class UpdateNoteRequest(val id: Long, val title: String?, val body: String?) {
fun toCommand(user: Long, rt: ActorRef<StatusReply<NoteResponse>>) = UpdateNote(user, id, title, body, rt)
data class UpdateNoteRequest(val id: String, val title: String?, val body: String?) {
fun toCommand(user: String, rt: ActorRef<StatusReply<NoteResponse>>) = UpdateNote(user, id, title, body, rt)
}

data class CreateNote(
val user: Long,
val user: String,
val title: String,
val body: String,
val replyTo: ActorRef<StatusReply<NoteResponse>>,
val id: Long = nextId()
val id: String = nextId()
) : Command {
fun toEvent() = NoteCreated(id, user, Encode.forHtml(title), Encode.forHtml(body))
}

data class UpdateNote(val user: Long, val id: Long, val title: String?, val body: String?, val replyTo: ActorRef<StatusReply<NoteResponse>>): Command {
data class UpdateNote(val user: String, val id: String, val title: String?, val body: String?, val replyTo: ActorRef<StatusReply<NoteResponse>>): Command {
fun toEvent() = NoteUpdated(id, user, title, body)
}

data class DeleteNote(val id: Long, val rt: ActorRef<StatusReply<Done>>): Command {
data class DeleteNote(val id: String, val rt: ActorRef<StatusReply<Done>>): Command {
fun toEvent() = NoteDeleted(id)
}

data class NoteCreated(val id: Long, val user: Long, val title: String, val body: String) : Event {
data class NoteCreated(val id: String, val user: String, val title: String, val body: String) : Event {
fun toEntity() = Note(id, user, title, slugify(title), body)
fun toResponse() = this.toEntity().toResponse()
}

data class NoteUpdated(val id: Long, val user: Long, val title: String?, val body: String?): Event
data class NoteUpdated(val id: String, val user: String, val title: String?, val body: String?): Event

data class NoteDeleted(val id: Long): Event
data class NoteDeleted(val id: String): Event

data class Note(
override val id: Long,
val user: Long,
override val id: String,
val user: String,
val title: String,
val slug: String,
val body: String
): Entity {
constructor(id: Long, user: Long, title: String, body: String): this(id, user, title, slugify(title), body)
constructor(id: String, user: String, title: String, body: String): this(id, user, title, slugify(title), body)
fun update(nu: NoteUpdated): Note = this.copy(title = nu.title ?: this.title, body = nu.body ?: this.body)
fun toResponse() = NoteResponse(id, user, DTF.format(LocalDateTime.ofInstant(id.toTSID().instant, ZoneId.of("CET"))), title, body)
fun toResponse() = NoteResponse(id, user, DTF.format(LocalDateTime.ofInstant(TSID.from(id).instant, ZoneId.of("CET"))), title, body)
}

data class NoteResponse(
val id: Long,
val user: Long,
val id: String,
val user: String,
val created: String,
val title: String,
val body: String
Expand All @@ -93,12 +94,12 @@ fun Route.notesRoute(processor: ActorRef<Command>, reader: Reader, scheduler: Sc
call.respond(reader.allNotes(rows, start).map { it.toResponse() })
}
get("{id?}") {
val id = call.parameters["id"]?.toLong() ?: return@get call.respond(HttpStatusCode.NotFound, "note id not specified")
val note: Note = reader.find(id) ?: return@get call.respond(HttpStatusCode.NotFound, "note not found for $id")
val id = call.parameters["id"] ?: return@get call.respond(HttpStatusCode.NotFound, "note id not specified")
val note = reader.findNote(id) ?: return@get call.respond(HttpStatusCode.NotFound, "note not found for $id")
call.respond(note.toResponse())
}
delete("{id?}") {
val id = call.parameters["id"]?.toLong() ?: return@delete call.respond(HttpStatusCode.NotFound, "note id not specified")
val id = call.parameters["id"] ?: return@delete call.respond(HttpStatusCode.NotFound, "note id not specified")
reader.findNote(id) ?: return@delete call.respond(HttpStatusCode.NotFound, "note not found for $id")
AskPattern.ask(processor, { rt -> DeleteNote(id, rt) }, timeout, scheduler).await().let {
when {
Expand Down
32 changes: 16 additions & 16 deletions src/main/kotlin/blog/model/State.kt
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
package blog.model

data class State(
private val users: Map<Long, User> = mapOf(),
private val notes: Map<Long, Note> = mapOf(),
private val tasks: Map<Long, Task> = mapOf(),
private val tags : Map<Long, Tag> = mapOf(),
private val users: Map<String, User> = mapOf(),
private val notes: Map<String, Note> = mapOf(),
private val tasks: Map<String, Task> = mapOf(),
private val tags : Map<String, Tag> = mapOf(),
private val recovered: Boolean = false
) {
fun save(u: User) : State = this.copy(users = this.users.minus(u.id).plus(u.id to u))
fun findUser(id: Long) : User? = users[id]
fun findUser(id: String) : User? = users[id]
fun findUserByEmail(email: String): User? = users.values.find { it.email == email }
fun allUsers() : List<User> = users.values.toList()
fun userCount() : Int = users.size
fun deleteUser(id: Long) : State = this.copy(users = this.users.minus(id))
fun deleteUser(id: String) : State = this.copy(users = this.users.minus(id))

fun save(n: Note) : State = this.copy(notes = this.notes.minus(n.id).plus(n.id to n))
fun findNote(id: Long) : Note? = notes[id]
fun findNotesForUser(id: Long): List<Note> = notes.values.filter { it.user == id }
fun noteCount() : Int = notes.size
fun deleteNote(id: Long) : State = this.copy(notes = this.notes.minus(id))
fun save(n: Note) : State = this.copy(notes = this.notes.minus(n.id).plus(n.id to n))
fun findNote(id: String) : Note? = notes[id]
fun findNotesForUser(id: String): List<Note> = notes.values.filter { it.user == id }
fun noteCount() : Int = notes.size
fun deleteNote(id: String) : State = this.copy(notes = this.notes.minus(id))

fun save(t: Task) : State = this.copy(tasks = this.tasks.minus(t.id).plus(t.id to t))
fun findTask(id: Long) : Task? = tasks[id]
fun findTasksForUser(id: Long): List<Task> = tasks.values.filter { it.user == id }
fun taskCount() : Int = tasks.size
fun deleteTask(id: Long) : State = this.copy(tasks = this.tasks.minus(id))
fun save(t: Task) : State = this.copy(tasks = this.tasks.minus(t.id).plus(t.id to t))
fun findTask(id: String) : Task? = tasks[id]
fun findTasksForUser(id: String): List<Task> = tasks.values.filter { it.user == id }
fun taskCount() : Int = tasks.size
fun deleteTask(id: String) : State = this.copy(tasks = this.tasks.minus(id))

fun setRecovered(): State = this.copy(recovered = true)
}
8 changes: 4 additions & 4 deletions src/main/kotlin/blog/model/Tag.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ package blog.model
import akka.actor.typed.ActorRef
import akka.pattern.StatusReply

data class Tag(override val id: Long, val label: String): Entity {
data class Tag(override val id: String, val label: String): Entity {
fun toResponse() = TagResponse(id, label)
}

data class CreateTagReq(val label: String) {
fun toCommand(replyTo: ActorRef<StatusReply<TagResponse>>) = CreateTag(nextId(), label, replyTo)
}

data class TagResponse(override val id: Long, val label: String): Response
data class TagResponse(override val id: String, val label: String): Response

data class CreateTag(val id: Long, val label: String, val replyTo: ActorRef<StatusReply<TagResponse>>): Command {
data class CreateTag(val id: String, val label: String, val replyTo: ActorRef<StatusReply<TagResponse>>): Command {
fun toEvent() = TagCreated(id, label)
}

data class TagCreated(val id: Long, val label: String): Event {
data class TagCreated(val id: String, val label: String): Event {
fun toEntity() = Tag(id, label)
}
48 changes: 24 additions & 24 deletions src/main/kotlin/blog/model/Task.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import java.time.LocalDateTime
import akka.Done
import akka.actor.typed.ActorRef
import akka.actor.typed.Scheduler
import akka.actor.typed.javadsl.AskPattern
import akka.actor.typed.javadsl.AskPattern.ask
import akka.pattern.StatusReply
import io.ktor.http.*
import io.ktor.server.application.*
Expand All @@ -20,8 +20,8 @@ enum class TaskStatus {
}

data class Task(
override val id: Long,
val user: Long,
override val id: String,
val user: String,
val title: String,
val slug: String,
val body: String,
Expand All @@ -44,28 +44,28 @@ data class Task(
}

data class CreateTaskRequest(val title: String, val body: String, val due: LocalDateTime): Request {
fun toCommand(user: Long, replyTo: ActorRef<StatusReply<TaskResponse>>) = CreateTask(user, title, body, due, replyTo)
fun toCommand(user: String, replyTo: ActorRef<StatusReply<TaskResponse>>) = CreateTask(user, title, body, due, replyTo)
}

data class UpdateTaskRequest(val id: Long, val title: String?, val body: String?, val due: LocalDateTime?, val status: TaskStatus?): Request {
fun toCommand(user: Long, replyTo: ActorRef<StatusReply<TaskResponse>>) = UpdateTask(user, id, title, body, due, status, replyTo)
data class UpdateTaskRequest(val id: String, val title: String?, val body: String?, val due: LocalDateTime?, val status: TaskStatus?): Request {
fun toCommand(user: String, replyTo: ActorRef<StatusReply<TaskResponse>>) = UpdateTask(user, id, title, body, due, status, replyTo)
}


data class CreateTask(
val user: Long,
val user: String,
val title: String,
val body: String,
val due: LocalDateTime,
val replyTo: ActorRef<StatusReply<TaskResponse>>,
val id: Long = nextId()
val id: String = nextId()
) : Command {
fun toEvent() = TaskCreated(id, user, title, body, due)
}

data class UpdateTask(
val user: Long,
val id: Long,
val user: String,
val id: String,
val title: String?,
val body: String?,
val due: LocalDateTime?,
Expand All @@ -75,14 +75,14 @@ data class UpdateTask(
fun toEvent() = TaskUpdated(user, id, title, body, due, status)
}

data class DeleteTask(val id: Long, val replyTo: ActorRef<StatusReply<Done>>): Command {
data class DeleteTask(val id: String, val replyTo: ActorRef<StatusReply<Done>>): Command {
fun toEvent() = TaskDeleted(id)
}


data class TaskCreated(
val id: Long,
val user: Long,
val id: String,
val user: String,
val title: String,
val body: String,
val due: LocalDateTime
Expand All @@ -92,20 +92,20 @@ data class TaskCreated(
}

data class TaskUpdated(
val user: Long,
val id: Long,
val user: String,
val id: String,
val title: String?,
val body: String?,
val due: LocalDateTime?,
val status: TaskStatus?,
) : Event

data class TaskDeleted(val id: Long): Event
data class TaskDeleted(val id: String): Event


data class TaskResponse(
val id: Long,
val user: Long,
val id: String,
val user: String,
val title: String,
val body: String,
val due: String,
Expand All @@ -118,7 +118,7 @@ fun Route.tasksRoute(processor: ActorRef<Command>, reader: Reader, scheduler: Sc
post {
val ctr = call.receive<CreateTaskRequest>()
val userId = user(call) ?: return@post call.respond(HttpStatusCode.Unauthorized, "Unauthorized")
AskPattern.ask(processor, { rt -> ctr.toCommand(userId, rt) }, timeout, scheduler).await().let {
ask(processor, { rt -> ctr.toCommand(userId, rt) }, timeout, scheduler).await().let {
when {
it.isSuccess -> {
call.response.header("Location", "/api/tasks/${it.value.id}")
Expand All @@ -134,25 +134,25 @@ fun Route.tasksRoute(processor: ActorRef<Command>, reader: Reader, scheduler: Sc
call.respond(reader.allTasks(rows, start).map { it.toResponse() })
}
get("{id?}") {
val id = call.parameters["id"]?.toLong() ?: return@get call.respond(HttpStatusCode.NotFound, "no task id specified")
val id = call.parameters["id"] ?: return@get call.respond(HttpStatusCode.NotFound, "no task id specified")
(reader.find<Task>(id) ?: return@get call.respond(HttpStatusCode.NotFound, "task not found for $id")).let {
call.respond(it.toResponse())
}
}
put {
val utr = call.receive<UpdateTaskRequest>()
val userId = user(call) ?: return@put call.respond(HttpStatusCode.Unauthorized, "Unauthorized")
AskPattern.ask(processor, { rt -> utr.toCommand(userId, rt) }, timeout, scheduler).await().let {
ask(processor, { rt -> utr.toCommand(userId, rt) }, timeout, scheduler).await().let {
when {
it.isSuccess -> call.respond(HttpStatusCode.OK, it.value)
else -> call.respond(HttpStatusCode.BadRequest, it.error.localizedMessage)
}
}
}
delete("{id?}") {
val id = call.parameters["id"]?.toLong() ?: return@delete call.respond(HttpStatusCode.NotFound, "no task id specified")
reader.find<Task>(id) ?: return@delete call.respond(HttpStatusCode.NotFound, "task not found for $id")
AskPattern.ask(processor, { rt -> DeleteTask(id, rt) }, timeout, scheduler).await().let {
val id = call.parameters["id"] ?: return@delete call.respond(HttpStatusCode.NotFound, "no task id specified")
reader.findTask(id) ?: return@delete call.respond(HttpStatusCode.NotFound, "task not found for $id")
ask(processor, { rt -> DeleteTask(id, rt) }, timeout, scheduler).await().let {
when {
it.isSuccess -> call.respond(HttpStatusCode.OK, mapOf("task" to id))
else -> call.respond(HttpStatusCode.InternalServerError, it.error.localizedMessage)
Expand Down
Loading

0 comments on commit 01bbb7c

Please sign in to comment.