From 73a571e395cf174dfa44e6355d6301bfc6a614dc Mon Sep 17 00:00:00 2001
From: maryarm <45322329+maryarm@users.noreply.github.com>
Date: Sun, 22 Mar 2020 07:00:43 +0430
Subject: [PATCH] KTLN-18: Kotlin Microservice With Spring Boot (#8913)
---
spring-reactive-kotlin/pom.xml | 87 +++++++++++++++++++
.../HealthTrackerApplication.kt | 11 +++
.../config/DBConfiguration.kt | 28 ++++++
.../controller/HealthRecordController.kt | 44 ++++++++++
.../controller/ProfileController.kt | 15 ++++
.../model/AverageHealthStatus.kt | 3 +
.../bootmicroservice/model/HealthRecord.kt | 8 ++
.../bootmicroservice/model/Profile.kt | 8 ++
.../repository/HealthRecordRepository.kt | 13 +++
.../repository/ProfileRepository.kt | 8 ++
.../src/main/resources/application.yml | 1 +
.../controller/ProfileControllerTest.kt | 51 +++++++++++
12 files changed, 277 insertions(+)
create mode 100644 spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/HealthTrackerApplication.kt
create mode 100644 spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/config/DBConfiguration.kt
create mode 100644 spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/controller/HealthRecordController.kt
create mode 100644 spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/controller/ProfileController.kt
create mode 100644 spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/model/AverageHealthStatus.kt
create mode 100644 spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/model/HealthRecord.kt
create mode 100644 spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/model/Profile.kt
create mode 100644 spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/repository/HealthRecordRepository.kt
create mode 100644 spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/repository/ProfileRepository.kt
create mode 100644 spring-reactive-kotlin/src/main/resources/application.yml
create mode 100644 spring-reactive-kotlin/src/test/kotlin/com/baeldung/bootmicroservice/controller/ProfileControllerTest.kt
diff --git a/spring-reactive-kotlin/pom.xml b/spring-reactive-kotlin/pom.xml
index 3a693837df..cbb143f6ec 100644
--- a/spring-reactive-kotlin/pom.xml
+++ b/spring-reactive-kotlin/pom.xml
@@ -5,6 +5,7 @@
spring-reactive-kotlin
spring-reactive-kotlin
Demo project for Spring Boot
+
jar
@@ -23,6 +24,18 @@
org.springframework.boot
spring-boot-starter-webflux
+
+ org.springframework.boot.experimental
+ spring-boot-starter-data-r2dbc
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ io.r2dbc
+ r2dbc-h2
+
com.fasterxml.jackson.module
jackson-module-kotlin
@@ -38,21 +51,95 @@
reactor-test
test
+
+
+ org.springframework.boot.experimental
+ spring-boot-test-autoconfigure-r2dbc
+ test
+
+
+ io.projectreactor
+ reactor-test
+ test
+
+
+
+
+
+ org.springframework.boot.experimental
+ spring-boot-bom-r2dbc
+ 0.1.0.M3
+ pom
+ import
+
+
+
+
+ src/main/kotlin
+ src/test/kotlin
kotlin-maven-plugin
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ test-compile
+ test-compile
+
+ test-compile
+
+
+
org.jetbrains.kotlin
${kotlin.version}
-Xjsr305=strict
+ 1.8
+
+ spring
+ jpa
+
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-allopen
+ ${kotlin.version}
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-noarg
+ ${kotlin.version}
+
+
+
+ 1.8
+ 1.3.70
+ 2.2.5.RELEASE
+
+
+
+
+ spring-milestones
+ Spring Milestones
+ https://repo.spring.io/milestone
+
+
+
+
diff --git a/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/HealthTrackerApplication.kt b/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/HealthTrackerApplication.kt
new file mode 100644
index 0000000000..c70057b5de
--- /dev/null
+++ b/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/HealthTrackerApplication.kt
@@ -0,0 +1,11 @@
+package com.baeldung.bootmicroservice
+
+import org.springframework.boot.autoconfigure.SpringBootApplication
+import org.springframework.boot.runApplication
+
+@SpringBootApplication
+class HealthTrackerApplication
+
+fun main(args: Array) {
+ runApplication(*args)
+}
diff --git a/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/config/DBConfiguration.kt b/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/config/DBConfiguration.kt
new file mode 100644
index 0000000000..b14682cc5c
--- /dev/null
+++ b/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/config/DBConfiguration.kt
@@ -0,0 +1,28 @@
+package com.baeldung.bootmicroservice.config;
+
+import org.springframework.context.annotation.Configuration
+import org.springframework.data.r2dbc.core.DatabaseClient
+
+@Configuration
+class DBConfiguration(db: DatabaseClient) {
+ init {
+ val initDb = db.execute {
+ """ CREATE TABLE IF NOT EXISTS profile (
+ id SERIAL PRIMARY KEY,
+ first_name VARCHAR(20) NOT NULL,
+ last_name VARCHAR(20) NOT NULL,
+ birth_date DATE NOT NULL
+ );
+ CREATE TABLE IF NOT EXISTS health_record(
+ id SERIAL PRIMARY KEY,
+ profile_id LONG NOT NULL,
+ temperature DECIMAL NOT NULL,
+ blood_pressure DECIMAL NOT NULL,
+ heart_rate DECIMAL,
+ date DATE NOT NULL
+ );
+ """
+ }
+ initDb.then().subscribe()
+ }
+}
diff --git a/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/controller/HealthRecordController.kt b/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/controller/HealthRecordController.kt
new file mode 100644
index 0000000000..620f187b7b
--- /dev/null
+++ b/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/controller/HealthRecordController.kt
@@ -0,0 +1,44 @@
+package com.baeldung.bootmicroservice.controller
+
+import com.baeldung.bootmicroservice.model.AverageHealthStatus
+import com.baeldung.bootmicroservice.model.HealthRecord
+import com.baeldung.bootmicroservice.repository.HealthRecordRepository
+import org.springframework.web.bind.annotation.GetMapping
+import org.springframework.web.bind.annotation.PathVariable
+import org.springframework.web.bind.annotation.PostMapping
+import org.springframework.web.bind.annotation.RequestBody
+import org.springframework.web.bind.annotation.RestController
+import reactor.core.publisher.Mono
+
+@RestController
+class HealthRecordController(val repository: HealthRecordRepository) {
+
+ @PostMapping("/health/{profileId}/record")
+ fun storeHealthRecord(@PathVariable("profileId") profileId: Long, @RequestBody record: HealthRecord): Mono =
+ repository.save(HealthRecord(null
+ , profileId
+ , record.temperature
+ , record.bloodPressure
+ , record.heartRate
+ , record.date))
+
+ @GetMapping("/health/{profileId}/avg")
+ fun fetchHealthRecordAverage(@PathVariable("profileId") profileId: Long): Mono =
+ repository.findByProfileId(profileId)
+ .reduce(
+ AverageHealthStatus(0, 0.0, 0.0, 0.0)
+ , { s, r ->
+ AverageHealthStatus(s.cnt + 1
+ , s.temperature + r.temperature
+ , s.bloodPressure + r.bloodPressure
+ , s.heartRate + r.heartRate
+ )
+ }
+ ).map { s ->
+ AverageHealthStatus(s.cnt
+ , if (s.cnt != 0) s.temperature / s.cnt else 0.0
+ , if (s.cnt != 0) s.bloodPressure / s.cnt else 0.0
+ , if (s.cnt != 0) s.heartRate / s.cnt else 0.0)
+ }
+
+}
\ No newline at end of file
diff --git a/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/controller/ProfileController.kt b/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/controller/ProfileController.kt
new file mode 100644
index 0000000000..1dc3bcdc50
--- /dev/null
+++ b/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/controller/ProfileController.kt
@@ -0,0 +1,15 @@
+package com.baeldung.bootmicroservice.controller
+
+import com.baeldung.bootmicroservice.model.Profile
+import com.baeldung.bootmicroservice.repository.ProfileRepository
+import org.springframework.web.bind.annotation.PostMapping
+import org.springframework.web.bind.annotation.RequestBody
+import org.springframework.web.bind.annotation.RestController
+import reactor.core.publisher.Mono
+
+@RestController
+class ProfileController(val repository: ProfileRepository) {
+
+ @PostMapping("/profile")
+ fun save(@RequestBody profile: Profile): Mono = repository.save(profile)
+}
\ No newline at end of file
diff --git a/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/model/AverageHealthStatus.kt b/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/model/AverageHealthStatus.kt
new file mode 100644
index 0000000000..3141146b9c
--- /dev/null
+++ b/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/model/AverageHealthStatus.kt
@@ -0,0 +1,3 @@
+package com.baeldung.bootmicroservice.model;
+
+class AverageHealthStatus(var cnt: Int, var temperature: Double, var bloodPressure: Double, var heartRate: Double)
diff --git a/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/model/HealthRecord.kt b/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/model/HealthRecord.kt
new file mode 100644
index 0000000000..71c534027f
--- /dev/null
+++ b/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/model/HealthRecord.kt
@@ -0,0 +1,8 @@
+package com.baeldung.bootmicroservice.model
+
+import org.springframework.data.annotation.Id
+import org.springframework.data.relational.core.mapping.Table
+import java.time.LocalDate
+
+@Table
+data class HealthRecord(@Id var id: Long?, var profileId: Long?, var temperature: Double, var bloodPressure: Double, var heartRate: Double, var date: LocalDate)
\ No newline at end of file
diff --git a/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/model/Profile.kt b/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/model/Profile.kt
new file mode 100644
index 0000000000..cbb7e675ea
--- /dev/null
+++ b/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/model/Profile.kt
@@ -0,0 +1,8 @@
+package com.baeldung.bootmicroservice.model
+
+import org.springframework.data.annotation.Id
+import org.springframework.data.relational.core.mapping.Table
+import java.time.LocalDateTime
+
+@Table
+data class Profile(@Id var id:Long?, var firstName : String, var lastName : String, var birthDate: LocalDateTime)
\ No newline at end of file
diff --git a/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/repository/HealthRecordRepository.kt b/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/repository/HealthRecordRepository.kt
new file mode 100644
index 0000000000..8cc91f06e4
--- /dev/null
+++ b/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/repository/HealthRecordRepository.kt
@@ -0,0 +1,13 @@
+package com.baeldung.bootmicroservice.repository
+
+import com.baeldung.bootmicroservice.model.HealthRecord
+import org.springframework.data.r2dbc.repository.Query
+import org.springframework.data.repository.reactive.ReactiveCrudRepository
+import org.springframework.stereotype.Repository
+import reactor.core.publisher.Flux
+
+@Repository
+interface HealthRecordRepository: ReactiveCrudRepository {
+ @Query("select p.* from health_record p where p.profile_id = :profileId ")
+ fun findByProfileId(profileId: Long): Flux
+}
\ No newline at end of file
diff --git a/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/repository/ProfileRepository.kt b/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/repository/ProfileRepository.kt
new file mode 100644
index 0000000000..eee8c5fcbe
--- /dev/null
+++ b/spring-reactive-kotlin/src/main/kotlin/com/baeldung/bootmicroservice/repository/ProfileRepository.kt
@@ -0,0 +1,8 @@
+package com.baeldung.bootmicroservice.repository
+
+import com.baeldung.bootmicroservice.model.Profile
+import org.springframework.data.repository.reactive.ReactiveCrudRepository
+import org.springframework.stereotype.Repository
+
+@Repository
+interface ProfileRepository: ReactiveCrudRepository
\ No newline at end of file
diff --git a/spring-reactive-kotlin/src/main/resources/application.yml b/spring-reactive-kotlin/src/main/resources/application.yml
new file mode 100644
index 0000000000..d75683f905
--- /dev/null
+++ b/spring-reactive-kotlin/src/main/resources/application.yml
@@ -0,0 +1 @@
+management.endpoints.web.exposure.include: health,metrics
\ No newline at end of file
diff --git a/spring-reactive-kotlin/src/test/kotlin/com/baeldung/bootmicroservice/controller/ProfileControllerTest.kt b/spring-reactive-kotlin/src/test/kotlin/com/baeldung/bootmicroservice/controller/ProfileControllerTest.kt
new file mode 100644
index 0000000000..51481af3d7
--- /dev/null
+++ b/spring-reactive-kotlin/src/test/kotlin/com/baeldung/bootmicroservice/controller/ProfileControllerTest.kt
@@ -0,0 +1,51 @@
+package com.baeldung.bootmicroservice.controller;
+
+import com.baeldung.bootmicroservice.model.Profile
+import com.fasterxml.jackson.databind.ObjectMapper
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.http.MediaType
+import org.springframework.test.web.reactive.server.WebTestClient
+import java.time.LocalDateTime
+
+@SpringBootTest
+class ProfileControllerTest {
+ @Autowired
+ lateinit var controller: ProfileController
+
+ @Autowired
+ lateinit var mapper: ObjectMapper ;
+
+ lateinit var client: WebTestClient
+ lateinit var profile: String
+
+ @BeforeEach
+ fun setup() {
+ client = WebTestClient.bindToController(controller).build()
+ profile = mapper.writeValueAsString(Profile(null, "kotlin", "reactive", LocalDateTime.now()))
+ }
+
+ @Test
+ fun whenRequestProfile_thenStatusShouldBeOk() {
+ client.post()
+ .uri("/profile")
+ .contentType(MediaType.APPLICATION_JSON)
+ .bodyValue(profile)
+ .exchange()
+ .expectStatus().isOk
+ }
+
+ @Test
+ fun whenRequestProfile_thenIdShouldBeNotNull() {
+ client.post()
+ .uri("/profile")
+ .contentType(MediaType.APPLICATION_JSON)
+ .bodyValue(profile)
+ .exchange()
+ .expectBody()
+ .jsonPath("$.id")
+ .isNotEmpty
+ }
+}