Skip to content

Commit 7617095

Browse files
committed
ktor and thymeleaf app
1 parent 436b5f1 commit 7617095

File tree

12 files changed

+374
-1
lines changed

12 files changed

+374
-1
lines changed

kotlin-ktor/build.gradle.kts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ description = "Example usage of Gradle plugin to generate GraphQL Kotlin Client"
44

55
val graphQLKotlinVersion = "7.0.1"
66
val ktorVersion = "2.3.5"
7+
val logbackVersion = "1.4.14"
8+
val kotlinTestUnit = "1.9.10"
9+
val seleniumVersion = "4.16.1"
710

811
repositories {
912
mavenCentral()
@@ -18,10 +21,15 @@ dependencies {
1821
implementation("io.ktor", "ktor-server-netty", ktorVersion)
1922
implementation("io.ktor", "ktor-server-websockets", ktorVersion)
2023

24+
implementation("io.ktor", "ktor-server-thymeleaf-jvm", ktorVersion)
25+
implementation("ch.qos.logback","logback-classic", logbackVersion)
26+
2127
testImplementation("io.ktor", "ktor-server-tests", ktorVersion)
2228
testImplementation("io.ktor", "ktor-client-content-negotiation", ktorVersion)
2329
testImplementation("io.ktor", "ktor-serialization-kotlinx-json", ktorVersion)
24-
testImplementation("org.jetbrains.kotlin", "kotlin-test-junit", "1.9.10")
30+
testImplementation("org.jetbrains.kotlin", "kotlin-test-junit", kotlinTestUnit)
31+
testImplementation("org.seleniumhq.selenium", "selenium-java", seleniumVersion)
32+
2533
}
2634

2735
plugins {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.baeldung.thymeleaf.data
2+
3+
import io.ktor.http.*
4+
5+
object DataHolder {
6+
7+
fun getStudentList() = studentsList
8+
9+
fun findStudentById(id: String?) = getStudentList().first { student -> student.id == id }
10+
11+
fun updateGrades(studentId: String?, parameters: Parameters) {
12+
findStudentById(studentId)
13+
.gradeList.forEach { grade ->
14+
grade.apply {
15+
value = parameters[grade.id] ?: "F"
16+
}
17+
}
18+
}
19+
20+
private val studentsList = listOf(
21+
Student(
22+
id = "1",
23+
firstName = "Michael",
24+
lastName = "Smith",
25+
gradeList = createGradeList()
26+
),
27+
Student(
28+
id = "2",
29+
firstName = "Mary",
30+
lastName = "Johnson",
31+
gradeList = createGradeList()
32+
),
33+
Student(
34+
id = "3",
35+
firstName = "John",
36+
lastName = "Doe",
37+
gradeList = createGradeList()
38+
),
39+
)
40+
41+
private fun createGradeList() = listOf(
42+
Grade(id = "1", subject = "Reading"),
43+
Grade(id = "2", subject = "Writing"),
44+
Grade(id = "3", subject = "Science"),
45+
Grade(id = "4", subject = "Mathematics"),
46+
)
47+
48+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.baeldung.thymeleaf.data
2+
3+
data class Grade (
4+
val id: String,
5+
val subject: String,
6+
var value: String = String()
7+
)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.baeldung.thymeleaf.data
2+
3+
data class Student(
4+
val id: String,
5+
val firstName: String,
6+
val lastName: String,
7+
val gradeList: List<Grade>,
8+
) {
9+
val fullName: String
10+
get() = "$firstName $lastName"
11+
12+
val hasAllGrades: Boolean
13+
get() = gradeList.firstOrNull { grade -> grade.value.isBlank() } == null
14+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.baeldung.thymeleaf.server
2+
3+
import com.baeldung.thymeleaf.server.plugins.configureRouting
4+
import com.baeldung.thymeleaf.server.plugins.configureTemplating
5+
import io.ktor.server.engine.*
6+
import io.ktor.server.netty.*
7+
8+
fun main() {
9+
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
10+
configureTemplating()
11+
configureRouting()
12+
}.start(wait = true)
13+
}
14+
15+
16+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.baeldung.thymeleaf.server.plugins
2+
3+
import com.baeldung.thymeleaf.data.DataHolder
4+
import io.ktor.server.application.*
5+
import io.ktor.server.request.*
6+
import io.ktor.server.response.*
7+
import io.ktor.server.routing.*
8+
import io.ktor.server.thymeleaf.*
9+
10+
fun Application.configureRouting() {
11+
routing {
12+
get("/") {
13+
call.respond(ThymeleafContent("index", mapOf("studentList" to DataHolder.getStudentList())))
14+
}
15+
get("/report-card/{id}") {
16+
call.respond(ThymeleafContent("report-card", mapOf("student" to DataHolder.findStudentById(call.parameters["id"]))))
17+
}
18+
post("/report-card/{id}") {
19+
val parameters = call.receiveParameters()
20+
DataHolder.updateGrades(call.parameters["id"], parameters)
21+
call.respondRedirect("/", false)
22+
}
23+
}
24+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.baeldung.thymeleaf.server.plugins
2+
3+
import io.ktor.server.application.*
4+
import io.ktor.server.thymeleaf.*
5+
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver
6+
7+
fun Application.configureTemplating() {
8+
install(Thymeleaf) {
9+
setTemplateResolver(ClassLoaderTemplateResolver().apply {
10+
prefix = "templates/"
11+
suffix = ".html"
12+
characterEncoding = "utf-8"
13+
})
14+
}
15+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<!DOCTYPE html >
2+
<html xmlns:th="http://www.thymeleaf.org">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Students</title>
6+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
7+
rel="stylesheet"
8+
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
9+
crossorigin="anonymous">
10+
</head>
11+
<body>
12+
<div class="container">
13+
<h2>Students</h2>
14+
<ul th:each="student : ${studentList}" class="list-group">
15+
<a th:href="@{'report-card/'+${student.id}}"
16+
th:text="${student.fullName}"
17+
th:classappend="${student.hasAllGrades} ? list-group-item-success : list-group-item-warning"
18+
class="list-group-item list-group-item-action"/>
19+
</ul>
20+
</div>
21+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
22+
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
23+
crossorigin="anonymous"/>
24+
</body>
25+
</html>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<!DOCTYPE html >
2+
<html xmlns:th="http://www.thymeleaf.org">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Report Card</title>
6+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
7+
rel="stylesheet"
8+
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
9+
crossorigin="anonymous">
10+
</head>
11+
<body>
12+
<div class="container">
13+
<form action="#" th:action="@{'~/report-card/'+${student.id}}" method="post">
14+
<h3>Name: <span th:text="${student.fullName}"/></h3>
15+
<table class="table table-striped">
16+
<thead>
17+
<tr>
18+
<th>Subject</th>
19+
<th>Grade</th>
20+
</tr>
21+
</thead>
22+
<tbody>
23+
<tr th:each="grade : ${student.gradeList}">
24+
<td th:text="${grade.subject}"></td>
25+
<td><input type="text" th:attr="value = ${grade.value}" th:name="${grade.id}"></td>
26+
</tr>
27+
</tbody>
28+
</table>
29+
<button type="submit" class="btn btn-primary">Submit</button>
30+
</form>
31+
</div>
32+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
33+
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
34+
crossorigin="anonymous"/>
35+
</body>
36+
</html>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.baeldung.thymeleaf.data
2+
3+
import io.ktor.http.*
4+
import kotlin.test.Test
5+
import kotlin.test.assertEquals
6+
import kotlin.test.assertNotNull
7+
8+
class DataHolderUnitTest {
9+
10+
@Test
11+
fun `when getStudentList is called then should retrieve a list`(){
12+
val studentList = DataHolder.getStudentList()
13+
14+
assertEquals(3, studentList.size)
15+
}
16+
17+
@Test
18+
fun `when findStudentById is called then should return a specific Student`(){
19+
val student = DataHolder.findStudentById("2")
20+
21+
assertEquals("2", student.id)
22+
assertEquals("Mary", student.firstName)
23+
assertEquals("Johnson", student.lastName)
24+
assertNotNull(student.gradeList)
25+
assertEquals(4, student.gradeList.size)
26+
}
27+
28+
@Test
29+
fun `when updateGrades is called then should update grades for a specific Student`(){
30+
val parameters = ParametersBuilder()
31+
.apply {
32+
append("1", "A")
33+
append("2", "B")
34+
append("3", "C")
35+
append("4", "D")
36+
}.build()
37+
38+
DataHolder.updateGrades("1", parameters)
39+
40+
val student = DataHolder.findStudentById("1")
41+
assertNotNull(student.gradeList)
42+
assertEquals("A", student.gradeList.first { it.id == "1" }.value)
43+
assertEquals("B", student.gradeList.first { it.id == "2" }.value)
44+
assertEquals("C", student.gradeList.first { it.id == "3" }.value)
45+
assertEquals("D", student.gradeList.first { it.id == "4" }.value)
46+
}
47+
48+
}

0 commit comments

Comments
 (0)