Skip to content

Commit 15e5c50

Browse files
authored
Merge pull request #1 from nixos89/01-implement-flyway
Implementation of Flyway Automatic Migration and REST API
2 parents 19e1faf + 29a6b0e commit 15e5c50

26 files changed

+315
-316
lines changed

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Kotlin-Java Demo ToDo app
2+
3+
Simple ToDo app containing REST API written in Java and Kotlin using:
4+
* Spring Data JPA
5+
* Flyway Migration Tool
6+
* PostgresDB 12 (scripts)
7+
* JUnit5
8+
9+
10+
## Steps for setting and running app:
11+
1. Create `todo_db` database in Postgres DB ver 12 using `postgres` both as **username** and **password**
12+
2. Enter directory of `first-java-kotlin-gradle-project` module
13+
3. Make sure property `spring.flyway.enabled` is set to `true` located in `src/main/resources/application.properties`
14+
4. Execute `./gradlew bootRun` command
15+
5. Before 2nd and every other app run (i.e. before executing Step4) set `spring.flyway.enabled` property to `false` located in `src/main/resources/application.properties`
16+
17+
## Target one of following endpoints:
18+
19+
1. **GET** all Todo(s) [http://localhost:8080/api/todos](http://localhost:8080/api/todos) <br/>
20+
2. **GET** Single Todo (with id=1) [http://localhost:8080/api/todos/1](http://localhost:8080/api/todos/1) <br/>
21+
3. **POST** Todo [http://localhost:8080/api/todos](http://localhost:8080/api/todos) <br/>
22+
...using following body
23+
```json
24+
{
25+
"title": "Kotlin app review",
26+
"description": "Survive the Kotlin app review"
27+
}
28+
```
29+
4. **PATCH** Todo [http://localhost:8080/api/todos/3](http://localhost:8080/api/todos/3) <br/>
30+
...using following body
31+
```json
32+
{
33+
"title": "Push created Kotlin project!!!"
34+
}
35+
```
36+
5. **DELETE** Todo [http://localhost:8080/api/todos/2](http://localhost:8080/api/todos/2) <br/>

build.gradle.kts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ dependencies {
3232
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
3333
implementation("org.jetbrains.kotlin:kotlin-reflect")
3434
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
35+
implementation("org.junit.jupiter:junit-jupiter:5.7.0")
3536
developmentOnly("org.springframework.boot:spring-boot-devtools")
37+
implementation("org.springframework.boot:spring-boot-starter-data-jdbc")
3638
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
3739
testImplementation("org.springframework.boot:spring-boot-starter-test") {
3840
exclude(module = "junit")
@@ -43,9 +45,10 @@ dependencies {
4345
testImplementation("com.ninja-squad:springmockk:3.0.1")
4446
implementation("org.springframework.boot:spring-boot-starter-mustache")
4547
implementation("org.springframework.boot:spring-boot-starter-actuator")
46-
runtimeOnly("com.h2database:h2")
48+
runtimeOnly("org.postgresql:postgresql")
49+
implementation( "org.flywaydb:flyway-core:7.9.1")
4750
kapt("org.springframework.boot:spring-boot-configuration-processor")
48-
// runtimeOnly("org.postgresql:postgresql")
51+
implementation("org.springframework.boot:spring-boot-configuration-processor")
4952
}
5053

5154
tasks.withType<KotlinCompile> {
Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,15 @@
11
package org.example.java_kotlin;
22

3-
import org.example.java_kotlin.config.BlogProperties;
4-
import org.springframework.boot.CommandLineRunner;
3+
import org.example.java_kotlin.config.TodoConfiguration;
54
import org.springframework.boot.SpringApplication;
65
import org.springframework.boot.autoconfigure.SpringBootApplication;
76
import org.springframework.boot.context.properties.EnableConfigurationProperties;
8-
import org.springframework.context.ApplicationContext;
9-
import org.springframework.context.annotation.Bean;
10-
11-
import java.util.Arrays;
127

138
@SpringBootApplication
14-
@EnableConfigurationProperties(BlogProperties.class)
9+
@EnableConfigurationProperties(TodoConfiguration.class)
1510
public class FirstJavaKotlinGradleApp {
1611

1712
public static void main(String[] args) {
1813
SpringApplication.run(FirstJavaKotlinGradleApp.class, args);
1914
}
20-
21-
@Bean
22-
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
23-
return args -> {
24-
25-
System.out.println("Let's inspect the beans provided by Spring Boot:");
26-
27-
String[] beanNames = ctx.getBeanDefinitionNames();
28-
Arrays.sort(beanNames);
29-
for (String beanName : beanNames) {
30-
System.out.println(beanName);
31-
}
32-
33-
};
34-
}
3515
}

src/main/java/org/example/java_kotlin/HelloController.java

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/main/java/org/example/java_kotlin/config/BlogConfiguration.kt

Lines changed: 0 additions & 37 deletions
This file was deleted.

src/main/java/org/example/java_kotlin/config/BlogProperties.kt

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.example.java_kotlin.config;
2+
3+
import com.zaxxer.hikari.HikariConfig;
4+
import com.zaxxer.hikari.HikariDataSource;
5+
import org.flywaydb.core.Flyway;
6+
import org.springframework.beans.factory.annotation.Value;
7+
import org.springframework.boot.context.properties.ConfigurationProperties;
8+
import org.springframework.context.annotation.Bean;
9+
import org.springframework.context.annotation.Configuration;
10+
11+
import javax.sql.DataSource;
12+
13+
@Configuration
14+
@ConfigurationProperties("spring.flyway")
15+
public class TodoConfiguration {
16+
17+
@Bean
18+
public DataSource datasource(
19+
@Value("${spring.datasource.url}") String url,
20+
@Value("${spring.datasource.username}") String username,
21+
@Value("${spring.datasource.password}") String password,
22+
@Value("${spring.datasource.driverClassName}") String driver) {
23+
HikariConfig config = new HikariConfig();
24+
25+
Flyway flyway = Flyway.configure().dataSource(url, username, password).load();
26+
flyway.migrate();
27+
28+
config.setJdbcUrl(url);
29+
config.setUsername(username);
30+
config.setPassword(password);
31+
config.setDriverClassName(driver);
32+
33+
return new HikariDataSource(config);
34+
}
35+
36+
37+
}

src/main/java/org/example/java_kotlin/controller/HtmlController.kt

Lines changed: 0 additions & 57 deletions
This file was deleted.

src/main/java/org/example/java_kotlin/controller/HttpControllers.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import org.springframework.web.bind.annotation.RequestMapping
99
import org.springframework.web.bind.annotation.RestController
1010
import org.springframework.web.server.ResponseStatusException
1111

12+
/* These classes are just part of SpringBoot Kotlin tutorial */
1213
@RestController
1314
@RequestMapping("/api/article")
1415
class ArticleController(private val articleRepository: ArticleRepository) {
Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,65 @@
11
package org.example.java_kotlin.controller
22

33
import org.example.java_kotlin.model.Todo
4+
import org.example.java_kotlin.repositories.TodoRepository
5+
import org.example.java_kotlin.service.TodoService
6+
import org.springframework.http.HttpStatus
7+
import org.springframework.http.ResponseEntity
8+
import org.springframework.util.ObjectUtils
9+
import org.springframework.validation.annotation.Validated
410
import org.springframework.web.bind.annotation.*
511

612
@RestController
7-
@RequestMapping("/todos")
8-
class TodoController {
13+
@RequestMapping("/api/todos")
14+
class TodoController(private val todoRepository: TodoRepository, private val todoService: TodoService) {
915

1016
@GetMapping("/{todoId}")
11-
fun getTodo(@PathVariable todoId: Long) {
12-
// TODO: implement `getTodo`
17+
fun getTodo(@PathVariable todoId: Long): ResponseEntity<Todo> {
18+
val todo = todoService.getTodoById(todoId)
19+
if (todo.isPresent) return ResponseEntity(todo.get(), HttpStatus.OK)
20+
return ResponseEntity(HttpStatus.NOT_FOUND)
1321
}
1422

1523
@GetMapping
16-
fun getAllTodos() {
17-
// TODO: implement `getAllTodos`
24+
fun getAllTodos(): ResponseEntity<List<Todo>> {
25+
val todos = todoService.getAllTodos().toList()
26+
if (todos.isNotEmpty()) return ResponseEntity(todos, HttpStatus.OK)
27+
return ResponseEntity(HttpStatus.NOT_FOUND)
1828
}
1929

2030
@PostMapping
21-
fun saveTodo(@RequestBody todo: Todo) {
22-
// TODO: implement `saveTodo`
31+
fun createTodo(@Validated @RequestBody todo: Todo): ResponseEntity<Void> {
32+
if (todoService.createTodo(todo)) {
33+
return ResponseEntity(HttpStatus.CREATED)
34+
}
35+
return ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR)
36+
}
37+
38+
@PutMapping("/{todoId}")
39+
fun updateTodo(@Validated @RequestBody todo: Todo, @PathVariable("todoId") todoId: Long): ResponseEntity<Void> {
40+
val todoUpdated = todoService.updateTodo(todo, todoId)
41+
if (ObjectUtils.isEmpty(todoUpdated)) {
42+
return ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR)
43+
}
44+
return ResponseEntity(HttpStatus.NO_CONTENT)
45+
46+
}
47+
48+
@PatchMapping("/{todoId}")
49+
fun partialUpdateTodo(@RequestBody todo: Todo, @PathVariable todoId: Long): ResponseEntity<Void> {
50+
val patchedTodo = todoService.patchTodo(todo, todoId)
51+
if (ObjectUtils.isEmpty(patchedTodo)) {
52+
return ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR)
53+
}
54+
return ResponseEntity(HttpStatus.NO_CONTENT)
55+
}
56+
57+
@DeleteMapping("/{todoId}")
58+
fun deleteTodo(@PathVariable todoId: Long): ResponseEntity<Void> {
59+
if (todoService.deleteTodo(todoId)) {
60+
return ResponseEntity(HttpStatus.NO_CONTENT)
61+
}
62+
return ResponseEntity(HttpStatus.NOT_FOUND)
2363
}
2464

2565
}

0 commit comments

Comments
 (0)