Skip to content
This repository was archived by the owner on Jul 13, 2021. It is now read-only.

Updated to Kotlin 1.3.50 #41

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .idea/runConfigurations/Backend____Jetty.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 11 additions & 11 deletions backend/build.gradle
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
group = 'org.jetbrains.demo.thinkter'
version = '0.0.1-SNAPSHOT'

apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'application'

dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"

compile "org.jetbrains.ktor:ktor-locations:$ktor_version"
compile "org.jetbrains.ktor:ktor-html-builder:$ktor_version"
compile "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
compile "io.ktor:ktor-locations:$ktor_version"
compile "io.ktor:ktor-html-builder:$ktor_version"
compile "org.ehcache:ehcache:3.0.0.m4"

compile "org.jetbrains.squash:squash-h2:$squash_version"

testCompile("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
testCompile "org.jetbrains.ktor:ktor-test-host:$ktor_version"
testCompile "io.ktor:ktor-server-test-host:$ktor_version"
testCompile "org.jsoup:jsoup:1.9.1"

compile "org.jetbrains.ktor:ktor-jetty:$ktor_version"
compile "io.ktor:ktor-gson:$ktor_version"
compile "io.ktor:ktor-server-jetty:$ktor_version"
compile group: 'com.google.code.gson', name: 'gson', version: '2.8.0'
}

Expand All @@ -35,10 +37,8 @@ compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}

kotlin {
experimental {
coroutines "enable"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}

mainClassName = 'org.jetbrains.ktor.jetty.DevelopmentHost'
mainClassName = 'io.ktor.server.jetty.EngineMain'
45 changes: 26 additions & 19 deletions backend/src/org/jetbrains/demo/thinkter/Application.kt
Original file line number Diff line number Diff line change
@@ -1,41 +1,48 @@
package org.jetbrains.demo.thinkter

import com.google.gson.*
import org.jetbrains.demo.thinkter.dao.*
import org.jetbrains.demo.thinkter.model.*
import org.jetbrains.ktor.application.*
import org.jetbrains.ktor.content.*
import org.jetbrains.ktor.features.*
import org.jetbrains.ktor.http.*
import org.jetbrains.ktor.locations.*
import org.jetbrains.ktor.logging.*
import org.jetbrains.ktor.routing.*
import org.jetbrains.ktor.sessions.*
import org.jetbrains.ktor.transform.*
import io.ktor.application.Application
import io.ktor.application.call
import io.ktor.application.install
import io.ktor.features.*
import io.ktor.gson.gson
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Locations
import io.ktor.response.respond
import io.ktor.routing.routing
import io.ktor.sessions.SessionTransportTransformerMessageAuthentication
import io.ktor.sessions.Sessions
import io.ktor.sessions.cookie
import org.jetbrains.demo.thinkter.dao.ThinkterDatabase

const val SESSION_NAME: String = "SESSION_NAME"

data class Session(val userId: String)

@KtorExperimentalLocationsAPI
fun Application.main() {
val storage = ThinkterDatabase(/*JDBCConnection.Companion.create(H2Dialect, pool)*/)

install(DefaultHeaders)
install(CallLogging)
install(ConditionalHeaders)
install(PartialContentSupport)
install(PartialContent)
install(Compression)
install(Locations)
install(StatusPages) {
exception<NotImplementedError> { call.respond(HttpStatusCode.NotImplemented) }
exception<NotImplementedError> { call.respond(HttpStatusCode.NotImplemented, "${it.message}") }
}

withSessions<Session> {
withCookieByValue {
settings = SessionCookiesSettings(transformers = listOf(SessionCookieTransformerMessageAuthentication(hashKey)))
install(Sessions) {
cookie<Session>(SESSION_NAME) {
transform(SessionTransportTransformerMessageAuthentication(hashKey))
}
}

transform.register<RpcData> {
TextContent(Gson().toJson(it), ContentType.Application.Json)
install(ContentNegotiation) {
gson {
setPrettyPrinting()
}
}

routing {
Expand Down
10 changes: 6 additions & 4 deletions backend/src/org/jetbrains/demo/thinkter/ApplicationPage.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.jetbrains.demo.thinkter

import io.ktor.html.Placeholder
import io.ktor.html.Template
import io.ktor.html.insert
import kotlinx.html.*
import org.jetbrains.ktor.html.*

class ApplicationPage : Template<HTML> {
val caption = Placeholder<TITLE>()
Expand All @@ -21,8 +23,8 @@ class ApplicationPage : Template<HTML> {
insert(head)
link("https://fonts.googleapis.com/icon?family=Material+Icons", rel = "stylesheet")

link(rel = LinkRel.stylesheet, type=LinkType.textCss, href = "http://yui.yahooapis.com/pure/0.6.0/pure-min.css")
link(rel = LinkRel.stylesheet, type=LinkType.textCss, href = "http://yui.yahooapis.com/pure/0.6.0/grids-responsive-min.css")
link(rel = LinkRel.stylesheet, type = LinkType.textCss, href = "http://yui.yahooapis.com/pure/0.6.0/pure-min.css")
link(rel = LinkRel.stylesheet, type = LinkType.textCss, href = "http://yui.yahooapis.com/pure/0.6.0/grids-responsive-min.css")

}
body {
Expand All @@ -45,7 +47,7 @@ class ApplicationPage : Template<HTML> {
}
}
div { id = "content" }
script(src = "frontend/frontend.bundle.js")
script(src = "frontend/frontend.bundle.js", block = {})
}
}
}
27 changes: 18 additions & 9 deletions backend/src/org/jetbrains/demo/thinkter/Delete.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
@file:Suppress("ImplicitThis")

package org.jetbrains.demo.thinkter

import org.jetbrains.demo.thinkter.dao.*
import org.jetbrains.demo.thinkter.model.*
import org.jetbrains.ktor.application.*
import org.jetbrains.ktor.http.*
import org.jetbrains.ktor.locations.*
import org.jetbrains.ktor.routing.*
import org.jetbrains.ktor.sessions.*

import io.ktor.application.call
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.get
import io.ktor.locations.post
import io.ktor.response.respond
import io.ktor.routing.Route
import io.ktor.sessions.get
import io.ktor.sessions.sessions
import org.jetbrains.demo.thinkter.dao.ThinkterStorage
import org.jetbrains.demo.thinkter.model.PostThoughtToken
import org.jetbrains.demo.thinkter.model.RpcData

@KtorExperimentalLocationsAPI
fun Route.delete(dao: ThinkterStorage, hashFunction: (String) -> String) {
get<ThoughtDelete> {
val user = call.sessionOrNull<Session>()?.let { dao.user(it.userId) }
val user = call.sessions.get<Session>()?.let { dao.user(it.userId) }
val date = System.currentTimeMillis()

if (user == null) {
Expand All @@ -22,7 +31,7 @@ fun Route.delete(dao: ThinkterStorage, hashFunction: (String) -> String) {
}

post<ThoughtDelete> {
val user = call.sessionOrNull<Session>()?.let { dao.user(it.userId) }
val user = call.sessions.get<Session>()?.let { dao.user(it.userId) }
val thought = dao.getThought(it.id)

if (user == null || thought.userId != user.userId || !call.verifyCode(it.date, user, it.code, hashFunction)) {
Expand Down
40 changes: 25 additions & 15 deletions backend/src/org/jetbrains/demo/thinkter/Index.kt
Original file line number Diff line number Diff line change
@@ -1,31 +1,41 @@
package org.jetbrains.demo.thinkter

import org.jetbrains.demo.thinkter.dao.*
import org.jetbrains.demo.thinkter.model.*
import org.jetbrains.ktor.application.*
import org.jetbrains.ktor.html.*
import org.jetbrains.ktor.http.*
import org.jetbrains.ktor.locations.*
import org.jetbrains.ktor.response.*
import org.jetbrains.ktor.routing.*
import org.jetbrains.ktor.sessions.*
import java.time.*
import io.ktor.application.call
import io.ktor.html.respondHtmlTemplate
import io.ktor.http.ContentType
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.get
import io.ktor.response.ApplicationSendPipeline
import io.ktor.response.etag
import io.ktor.response.respond
import io.ktor.routing.Route
import io.ktor.routing.accept
import io.ktor.routing.get
import io.ktor.sessions.get
import io.ktor.sessions.sessions
import org.jetbrains.demo.thinkter.dao.ThinkterStorage
import org.jetbrains.demo.thinkter.model.IndexResponse
import org.jetbrains.demo.thinkter.model.PollResponse
import org.jetbrains.demo.thinkter.model.Thought
import java.time.LocalDateTime
import java.time.ZoneId

@KtorExperimentalLocationsAPI
fun Route.index(storage: ThinkterStorage) {
contentType(ContentType.Text.Html) {
get<Index> {
accept(ContentType.Text.Html) {
get {
call.respondHtmlTemplate(ApplicationPage()) {
caption { +"Thinkter" }
}
}
}
contentType(ContentType.Application.Json) {
accept(ContentType.Application.Json) {
get<Index> {
val user = call.sessionOrNull<Session>()?.let { storage.user(it.userId) }
val user = call.sessions.get<Session>()?.let { storage.user(it.userId) }
val top = storage.top(10).map(storage::getThought)
val latest = storage.latest(10).map(storage::getThought)

call.response.pipeline.intercept(ApplicationResponsePipeline.After) {
call.response.pipeline.intercept(ApplicationSendPipeline.After) {
val etagString = user?.userId + "," + top.joinToString { it.id.toString() } + latest.joinToString { it.id.toString() }
call.response.etag(etagString)
}
Expand Down
28 changes: 15 additions & 13 deletions backend/src/org/jetbrains/demo/thinkter/Locations.kt
Original file line number Diff line number Diff line change
@@ -1,35 +1,37 @@
@file:Suppress("EXPERIMENTAL_API_USAGE")

package org.jetbrains.demo.thinkter

import org.jetbrains.ktor.locations.*
import io.ktor.locations.Location


@location("/")
class Index()
@Location("/")
class Index

@location("/poll")
@Location("/poll")
class Poll(val lastTime: String = "")

@location("/post-new")
@Location("/post-new")
data class PostThought(val text: String = "", val date: Long = 0L, val code: String = "", val replyTo: Int? = null)

@location("/thought/{id}/delete")
@Location("/thought/{id}/delete")
data class ThoughtDelete(val id: Int, val date: Long, val code: String)

@location("/thought/{id}")
@Location("/thought/{id}")
data class ViewThought(val id: Int)

@location("/user/{user}")
@Location("/user/{user}")
@Deprecated("")
data class UserPage(val user: String)

@location("/user/{user}/thoughts")
@Location("/user/{user}/thoughts")
data class UserThoughts(val user: String)

@location("/register")
@Location("/register")
data class Register(val userId: String = "", val displayName: String = "", val email: String = "", val password: String = "", val error: String = "")

@location("/login")
@Location("/login")
data class Login(val userId: String = "", val password: String = "", val error: String = "")

@location("/logout")
class Logout()
@Location("/logout")
class Logout
43 changes: 29 additions & 14 deletions backend/src/org/jetbrains/demo/thinkter/Login.kt
Original file line number Diff line number Diff line change
@@ -1,39 +1,54 @@
package org.jetbrains.demo.thinkter

import org.jetbrains.demo.thinkter.dao.*
import org.jetbrains.demo.thinkter.model.*
import org.jetbrains.ktor.application.*
import org.jetbrains.ktor.http.*
import org.jetbrains.ktor.locations.*
import org.jetbrains.ktor.routing.*
import org.jetbrains.ktor.sessions.*
import io.ktor.application.call
import io.ktor.http.HttpStatusCode
import io.ktor.http.Parameters
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.get
import io.ktor.locations.post
import io.ktor.request.receive
import io.ktor.response.respond
import io.ktor.routing.Route
import io.ktor.sessions.clear
import io.ktor.sessions.get
import io.ktor.sessions.sessions
import io.ktor.sessions.set
import org.jetbrains.demo.thinkter.dao.ThinkterStorage
import org.jetbrains.demo.thinkter.model.LoginResponse

@KtorExperimentalLocationsAPI
fun Route.login(dao: ThinkterStorage, hash: (String) -> String) {
get<Login> {
val user = call.sessionOrNull<Session>()?.let { dao.user(it.userId) }
val user = call.sessions.get<Session>()?.let { dao.user(it.userId) }
if (user == null) {
call.respond(HttpStatusCode.Forbidden)
} else {
call.respond(LoginResponse(user))
}
}
post<Login> {
val parameters = call.receive<Parameters>()
val form = Login(
userId = parameters["userId"] ?: "",
password = parameters["password"] ?: "",
error = parameters["error"] ?: ""
)
val login = when {
it.userId.length < 4 -> null
it.password.length < 6 -> null
!userNameValid(it.userId) -> null
else -> dao.user(it.userId, hash(it.password))
form.userId.length < 4 -> null
form.password.length < 6 -> null
!userNameValid(form.userId) -> null
else -> dao.user(form.userId, hash(form.password))
}

if (login == null) {
call.respond(LoginResponse(error = "Invalid username or password"))
} else {
call.session(Session(login.userId))
call.sessions.set(Session(login.userId))
call.respond(LoginResponse(login))
}
}
post<Logout> {
call.clearSession()
call.sessions.clear<Session>()
call.respond(HttpStatusCode.OK)
}
}
Loading