-
-
Notifications
You must be signed in to change notification settings - Fork 371
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
# Pull Request Added Kotlin Example for Spring Boot Fixes: #3963 ## Description Adds Example for Kotlin Spring Boot `6-hello-spring-boot` and `7-todo-spring-boot` ## Related Issues - Link to related issue #3963. ## Checklist - [x] New Example For Kotlin Spring Boot Hello World - [x] New Example for Kotlin Spring Boot Todo MVC ## Status Completed Addition
- Loading branch information
1 parent
db891bd
commit c193ce6
Showing
18 changed files
with
548 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package build | ||
import mill._, kotlinlib._ | ||
|
||
object `package` extends RootModule with KotlinModule { | ||
|
||
def kotlinVersion = "1.9.24" | ||
|
||
def mainClass = Some("com.example.HelloSpringBootKt") | ||
|
||
def ivyDeps = Agg( | ||
ivy"org.springframework.boot:spring-boot-starter-web:2.5.6", | ||
ivy"org.springframework.boot:spring-boot-starter-actuator:2.5.6" | ||
) | ||
|
||
object test extends KotlinTests with TestModule.Junit5 { | ||
def ivyDeps = super.ivyDeps() ++ Agg( | ||
ivy"org.springframework.boot:spring-boot-starter-test:2.5.6" | ||
) | ||
} | ||
} | ||
|
||
// This example demonstrates how to set up a simple Spring Boot webserver, | ||
// able to handle a single HTTP request at `/` and reply with a single response. | ||
|
||
/** Usage | ||
|
||
> mill test | ||
...com.example.HelloSpringBootTest#shouldReturnDefaultMessage() finished... | ||
|
||
> mill runBackground | ||
|
||
> curl http://localhost:8095 | ||
...<h1>Hello, World!</h1>... | ||
|
||
> mill clean runBackground | ||
|
||
*/ |
1 change: 1 addition & 0 deletions
1
example/kotlinlib/web/6-hello-spring-boot/resources/application.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
server.port=8095 |
19 changes: 19 additions & 0 deletions
19
example/kotlinlib/web/6-hello-spring-boot/src/com/example/HelloSpringBoot.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package com.example | ||
|
||
import org.springframework.boot.autoconfigure.SpringBootApplication | ||
import org.springframework.boot.runApplication | ||
import org.springframework.web.bind.annotation.GetMapping | ||
import org.springframework.web.bind.annotation.RestController | ||
|
||
@SpringBootApplication | ||
open class HelloSpringBoot | ||
|
||
fun main(args: Array<String>) { | ||
runApplication<HelloSpringBoot>(*args) | ||
} | ||
|
||
@RestController | ||
class HelloController { | ||
@GetMapping("/") | ||
fun hello(): String = "<h1>Hello, World!</h1>" | ||
} |
24 changes: 24 additions & 0 deletions
24
example/kotlinlib/web/6-hello-spring-boot/test/src/com/example/HelloSpringBootTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.example | ||
|
||
import org.junit.jupiter.api.Assertions.assertEquals | ||
import org.junit.jupiter.api.Test | ||
import org.springframework.beans.factory.annotation.Autowired | ||
import org.springframework.boot.test.context.SpringBootTest | ||
import org.springframework.boot.test.web.client.TestRestTemplate | ||
import org.springframework.boot.web.server.LocalServerPort | ||
|
||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) | ||
class HelloSpringBootTest { | ||
|
||
@LocalServerPort | ||
private var port: Int = 0 | ||
|
||
@Autowired | ||
private lateinit var restTemplate: TestRestTemplate | ||
|
||
@Test | ||
fun shouldReturnDefaultMessage() { | ||
val response = restTemplate.getForObject("http://localhost:$port/", String::class.java) | ||
assertEquals("<h1>Hello, World!</h1>", response) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package build | ||
import mill._, kotlinlib._ | ||
|
||
object `package` extends RootModule with KotlinModule { | ||
def kotlinVersion = "1.9.24" | ||
|
||
def mainClass = Some("com.example.TodomvcApplicationKt") | ||
def ivyDeps = Agg( | ||
ivy"org.springframework.boot:spring-boot-starter-data-jpa:2.5.4", | ||
ivy"org.springframework.boot:spring-boot-starter-thymeleaf:2.5.4", | ||
ivy"org.springframework.boot:spring-boot-starter-validation:2.5.4", | ||
ivy"org.springframework.boot:spring-boot-starter-web:2.5.4", | ||
ivy"org.jetbrains.kotlin:kotlin-reflect:2.0.21", | ||
ivy"javax.xml.bind:jaxb-api:2.3.1", | ||
ivy"org.webjars:webjars-locator:0.41", | ||
ivy"org.webjars.npm:todomvc-common:1.0.5", | ||
ivy"org.webjars.npm:todomvc-app-css:2.4.1" | ||
) | ||
|
||
trait HelloTests extends KotlinTests with TestModule.Junit5 { | ||
def mainClass = Some("com.example.TodomvcApplicationKt") | ||
def ivyDeps = super.ivyDeps() ++ Agg( | ||
ivy"org.springframework.boot:spring-boot-starter-test:2.5.6" | ||
) | ||
} | ||
|
||
object test extends HelloTests { | ||
def ivyDeps = super.ivyDeps() ++ Agg( | ||
ivy"com.h2database:h2:2.3.230" | ||
) | ||
} | ||
|
||
object integration extends HelloTests { | ||
def ivyDeps = super.ivyDeps() ++ Agg( | ||
ivy"org.testcontainers:testcontainers:1.18.0", | ||
ivy"org.testcontainers:junit-jupiter:1.18.0", | ||
ivy"org.testcontainers:postgresql:1.18.0", | ||
ivy"org.postgresql:postgresql:42.6.0" | ||
) | ||
} | ||
} | ||
|
||
// This is a larger example using Spring Boot, implementing the well known | ||
// https://todomvc.com/[TodoMVC] example app. Apart from running a webserver, | ||
// this example also demonstrates: | ||
// | ||
// * Serving HTML templates using Thymeleaf | ||
// * Serving static Javascript and CSS using Webjars | ||
// * Querying a SQL database using JPA and H2 | ||
// * Unit testing using a H2 in-memory database | ||
// * Integration testing using Testcontainers Postgres in Docker | ||
|
||
/** Usage | ||
|
||
> mill test | ||
...com.example.TodomvcTests#homePageLoads() finished... | ||
...com.example.TodomvcTests#addNewTodoItem() finished... | ||
|
||
> mill integration | ||
...com.example.TodomvcIntegrationTests#homePageLoads() finished... | ||
...com.example.TodomvcIntegrationTests#addNewTodoItem() finished... | ||
|
||
> mill test.runBackground | ||
|
||
> curl http://localhost:8099 | ||
...<h1>todos</h1>... | ||
|
||
> mill clean runBackground | ||
|
||
*/ |
3 changes: 3 additions & 0 deletions
3
example/kotlinlib/web/7-todo-spring-boot/integration/resources/application.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
spring.mvc.hiddenmethod.filter.enabled=true | ||
spring.jpa.hibernate.ddl-auto=update | ||
spring.datasource.driver-class-name=org.postgresql.Driver |
76 changes: 76 additions & 0 deletions
76
...e/kotlinlib/web/7-todo-spring-boot/integration/src/com/example/TodomvcIntegrationTests.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package com.example | ||
|
||
import org.assertj.core.api.Assertions.assertThat | ||
import org.junit.jupiter.api.Test | ||
import org.springframework.beans.factory.annotation.Autowired | ||
import org.springframework.boot.test.context.SpringBootTest | ||
import org.springframework.boot.test.web.client.TestRestTemplate | ||
import org.springframework.boot.web.server.LocalServerPort | ||
import org.springframework.http.HttpEntity | ||
import org.springframework.http.HttpHeaders | ||
import org.springframework.http.HttpMethod | ||
import org.springframework.http.MediaType | ||
import org.springframework.test.context.DynamicPropertyRegistry | ||
import org.springframework.test.context.DynamicPropertySource | ||
import org.testcontainers.containers.PostgreSQLContainer | ||
import org.testcontainers.junit.jupiter.Container | ||
import org.testcontainers.junit.jupiter.Testcontainers | ||
|
||
@Testcontainers | ||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) | ||
class TodomvcIntegrationTests { | ||
|
||
companion object { | ||
@Container | ||
val postgresContainer = PostgreSQLContainer<Nothing>("postgres:latest").apply { | ||
withDatabaseName("test") | ||
withUsername("test") | ||
withPassword("test") | ||
} | ||
|
||
@JvmStatic | ||
@DynamicPropertySource | ||
fun postgresProperties(registry: DynamicPropertyRegistry) { | ||
registry.add("spring.datasource.url", postgresContainer::getJdbcUrl) | ||
registry.add("spring.datasource.username", postgresContainer::getUsername) | ||
registry.add("spring.datasource.password", postgresContainer::getPassword) | ||
} | ||
} | ||
|
||
@LocalServerPort | ||
private var port: Int = 0 | ||
|
||
@Autowired | ||
private lateinit var restTemplate: TestRestTemplate | ||
|
||
@Test | ||
fun homePageLoads() { | ||
val response = restTemplate.getForEntity("http://localhost:$port/", String::class.java) | ||
assertThat(response.statusCode.is2xxSuccessful).isTrue | ||
assertThat(response.body).contains("<h1>todos</h1>") | ||
} | ||
|
||
@Test | ||
fun addNewTodoItem() { | ||
// Set up headers and form data for the POST request | ||
val headers = HttpHeaders().apply { | ||
contentType = MediaType.APPLICATION_FORM_URLENCODED | ||
} | ||
val newTodo = "title=Test+Todo" | ||
val entity = HttpEntity(newTodo, headers) | ||
|
||
// Send the POST request to add a new todo item | ||
val postResponse = restTemplate.exchange( | ||
"http://localhost:$port/", | ||
HttpMethod.POST, | ||
entity, | ||
String::class.java, | ||
) | ||
assertThat(postResponse.statusCode.is3xxRedirection).isTrue | ||
|
||
// Send a GET request to verify the new todo item was added | ||
val getResponse = restTemplate.getForEntity("http://localhost:$port/", String::class.java) | ||
assertThat(getResponse.statusCode.is2xxSuccessful).isTrue | ||
assertThat(getResponse.body).contains("Test Todo") | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
example/kotlinlib/web/7-todo-spring-boot/resources/templates/fragments.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" lang="en"> | ||
<li th:fragment="todoItem(item)" th:classappend="${item.completed?'completed':''}"> | ||
<div class="view"> | ||
<form th:action="@{/{id}/toggle(id=${item.id})}" th:method="put"> | ||
<input class="toggle" type="checkbox" | ||
onchange="this.form.submit()" | ||
th:attrappend="checked=${item.completed?'true':null}"> | ||
<label th:text="${item.title}">Taste JavaScript</label> | ||
</form> | ||
<form th:action="@{/{id}(id=${item.id})}" th:method="delete"> | ||
<button class="destroy"></button> | ||
</form> | ||
</div> | ||
<input class="edit" value="Create a TodoMVC template"> | ||
</li> | ||
</html> |
84 changes: 84 additions & 0 deletions
84
example/kotlinlib/web/7-todo-spring-boot/resources/templates/index.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
<!doctype html> | ||
<html xmlns="http://www.w3.org/1999/xhtml" | ||
xmlns:th="http://www.thymeleaf.org" | ||
lang="en"> | ||
<head> | ||
<meta charset="utf-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1"> | ||
<title>Template • TodoMVC</title> | ||
<link rel="stylesheet" th:href="@{/webjars/todomvc-common/base.css}"> | ||
<link rel="stylesheet" th:href="@{/webjars/todomvc-app-css/index.css}"> | ||
</head> | ||
<body> | ||
<section class="todoapp"> | ||
<header class="header"> | ||
<h1>todos</h1> | ||
<form th:action="@{/}" method="post" th:object="${item}"> | ||
<input class="new-todo" placeholder="What needs to be done?" autofocus | ||
th:field="*{title}"> | ||
</form> | ||
</header> | ||
<!-- This section should be hidden by default and shown when there are todos --> | ||
<section class="main" th:if="${totalItemCount > 0}"> | ||
<form th:action="@{/toggle-all}" th:method="put"> | ||
<input id="toggle-all" class="toggle-all" type="checkbox" | ||
onclick="this.form.submit()"> | ||
<label for="toggle-all">Mark all as complete</label> | ||
</form> | ||
<ul class="todo-list" th:remove="all-but-first"> | ||
<li th:insert="fragments :: todoItem(${item})" th:each="item : ${todoItems}" th:remove="tag"> | ||
</li> | ||
<!-- These are here just to show the structure of the list items --> | ||
<!-- List items should get the class `editing` when editing and `completed` when marked as completed --> | ||
<li class="completed"> | ||
<div class="view"> | ||
<input class="toggle" type="checkbox" checked> | ||
<label>Taste JavaScript</label> | ||
<button class="destroy"></button> | ||
</div> | ||
<input class="edit" value="Create a TodoMVC template"> | ||
</li> | ||
<li> | ||
<div class="view"> | ||
<input class="toggle" type="checkbox"> | ||
<label>Buy a unicorn</label> | ||
<button class="destroy"></button> | ||
</div> | ||
<input class="edit" value="Rule the web"> | ||
</li> | ||
</ul> | ||
</section> | ||
<!-- This footer should be hidden by default and shown when there are todos --> | ||
<footer class="footer" th:if="${totalItemCount > 0}"> | ||
<th:block th:unless="${activeItemCount == 1}"> | ||
<span class="todo-count"><strong th:text="${activeItemCount}">0</strong> items left</span> | ||
</th:block> | ||
<th:block th:if="${activeItemCount == 1}"> | ||
<span class="todo-count"><strong>1</strong> item left</span> | ||
</th:block> | ||
<ul class="filters"> | ||
<li> | ||
<a th:href="@{/}" | ||
th:classappend="${todoFilter.name() == 'ALL'?'selected':''}">All</a> | ||
</li> | ||
<li> | ||
<a th:href="@{/active}" | ||
th:classappend="${todoFilter.name() == 'ACTIVE'?'selected':''}">Active</a> | ||
</li> | ||
<li> | ||
<a th:href="@{/completed}" | ||
th:classappend="${todoFilter.name() == 'COMPLETED'?'selected':''}">Completed</a> | ||
</li> | ||
</ul> | ||
<form th:action="@{/completed}" th:method="delete" | ||
th:if="${completedItemCount > 0}"> | ||
<button class="clear-completed">Clear completed</button> | ||
</form> | ||
</footer> | ||
</section> | ||
<footer class="info"> | ||
<p>Double-click to edit a todo</p> | ||
</footer> | ||
<script th:src="@{/webjars/todomvc-common/base.js}"></script> | ||
</body> | ||
</html> |
11 changes: 11 additions & 0 deletions
11
example/kotlinlib/web/7-todo-spring-boot/src/com/example/TodomvcApplication.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.example | ||
|
||
import org.springframework.boot.SpringApplication | ||
import org.springframework.boot.autoconfigure.SpringBootApplication | ||
|
||
@SpringBootApplication | ||
open class TodomvcApplication | ||
|
||
fun main(args: Array<String>) { | ||
SpringApplication.run(TodomvcApplication::class.java, *args) | ||
} |
18 changes: 18 additions & 0 deletions
18
example/kotlinlib/web/7-todo-spring-boot/src/com/example/todoitem/TodoItem.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package com.example.todoitem | ||
|
||
import javax.persistence.Entity | ||
import javax.persistence.GeneratedValue | ||
import javax.persistence.Id | ||
import javax.validation.constraints.NotBlank | ||
|
||
@Entity | ||
data class TodoItem( | ||
@Id | ||
@GeneratedValue | ||
val id: Long? = null, | ||
|
||
@field:NotBlank | ||
var title: String = "", | ||
|
||
var completed: Boolean = false, | ||
) |
7 changes: 7 additions & 0 deletions
7
...le/kotlinlib/web/7-todo-spring-boot/src/com/example/todoitem/TodoItemNotFoundException.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.example.todoitem | ||
|
||
import org.springframework.http.HttpStatus | ||
import org.springframework.web.bind.annotation.ResponseStatus | ||
|
||
@ResponseStatus(HttpStatus.NOT_FOUND) | ||
class TodoItemNotFoundException(itemId: Long) : RuntimeException("TodoItem itemId=$itemId not found") |
Oops, something went wrong.