v0.0.5: db update, API customer_information

introducing AOP
This commit is contained in:
Bossanyi Tibor
2020-06-01 09:26:41 +02:00
parent 5c85e28269
commit 7ef6f50c3b
30 changed files with 1538 additions and 146 deletions
@@ -4,7 +4,6 @@ import org.slf4j.LoggerFactory
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@@ -12,19 +11,14 @@ import org.springframework.web.bind.annotation.RestController
@RestController
class ApiApplication
private val logger = LoggerFactory.getLogger(ApiApplication::class.simpleName)
@Override
fun configure(application: SpringApplicationBuilder): SpringApplicationBuilder {
return application.sources(ApiApplication::class.java)
}
fun main(args: Array<String>) {
logger.info(" ---- Start aitrainer API")
SpringApplication.run(ApiApplication::class.java, *args)
}
@RequestMapping( "/")
fun hello(): String {
return "Hello Aitrainer API"
}
@@ -0,0 +1,45 @@
package com.aitrainer.api.controller
import org.springframework.beans.factory.annotation.Value
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping()
class ApplicationProperties {
@Value("\${application.version}")
private lateinit var version: String
@Value("\${spring.datasource.url}")
private lateinit var datasourceUrl: String
@Value("\${spring.datasource.username}")
private lateinit var datasourceUsername: String
@Value("\${spring.datasource.password}")
private lateinit var datasourcePassword: String
@GetMapping("/version")
fun getVersion(): String {
return this.version
}
@GetMapping("/datasourceUrl")
fun getDatasourceUrl(): String {
return this.datasourceUrl
}
@GetMapping("/datasourceUsername")
fun getDatasourceUsername(): String {
return this.datasourceUsername
}
@GetMapping("/datasourcePassword")
fun getDatasourcePassword(): String {
return this.datasourcePassword
}
}
@@ -0,0 +1,27 @@
package com.aitrainer.api.controller
import com.aitrainer.api.model.Configuration
import com.aitrainer.api.repository.ConfigurationRepository
import org.springframework.web.bind.annotation.RestController
import java.time.LocalDateTime
@RestController
class ConfigurationController ( private val configurationRepository: ConfigurationRepository) {
fun getConfiguration(key: String): Configuration =
configurationRepository.findByConfigKey(key)
fun updateConfiguration( newConfiguration: Configuration): Configuration {
val existingConfiguration: Configuration = configurationRepository.findByConfigKey(newConfiguration.configKey)
val updatedConfiguration: Configuration = existingConfiguration.copy(
configKey = newConfiguration.configKey,
configValue = newConfiguration.configValue,
//dateAdd = newConfiguration.dateAdd,
dateChange = LocalDateTime.now().toString())
configurationRepository.save(updatedConfiguration)
return existingConfiguration
}
}
@@ -0,0 +1,34 @@
package com.aitrainer.api.controller
import com.aitrainer.api.ApiApplication
import com.aitrainer.api.repository.ConfigurationRepository
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before
import org.aspectj.lang.annotation.Pointcut
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
@Suppress("unused")
@Aspect
@Component
class CustomerControllerAspect {
private val logger = LoggerFactory.getLogger(ApiApplication::class.simpleName)
@Autowired
private lateinit var configurationRepository: ConfigurationRepository
@Autowired
private lateinit var properties: ApplicationProperties
@Suppress("unused")
@Pointcut("execution(* com.aitrainer.api.controller.CustomerController.*())")
fun customerControllerAspect() {
}
@Before("customerControllerAspect()")
fun loggingAop() {
Singleton.checkDBUpdate(configurationRepository, properties)
}
}
@@ -0,0 +1,18 @@
package com.aitrainer.api.controller
import com.aitrainer.api.model.CustomerInformation
import com.aitrainer.api.repository.CustomerInformationRepository
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import java.time.LocalDateTime
@RestController
@RequestMapping("/api")
class CustomerInformationController( private val customerInformationRepository: CustomerInformationRepository ) {
@GetMapping("/customer_information")
fun getCustomerInformation(dateTime: String): List<CustomerInformation> =
customerInformationRepository.findByDisplayBeginLessThanAndDisplayEndGreaterThan(dateTime, dateTime )
}
@@ -0,0 +1,105 @@
package com.aitrainer.api.controller
import com.aitrainer.api.ApiApplication
import com.aitrainer.api.model.Configuration
import com.aitrainer.api.repository.ConfigurationRepository
import org.hibernate.Hibernate.isInitialized
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.RestController
import java.io.File
import java.sql.Connection
import java.sql.DriverManager
import java.sql.ResultSet
import java.util.Properties
@RestController
object Singleton {
private lateinit var connection: Connection
private lateinit var configurationRepository: ConfigurationRepository
private lateinit var properties: ApplicationProperties
private var initialized: Boolean = false
private val logger = LoggerFactory.getLogger(ApiApplication::class.simpleName)
// to use the singleton functionality avoiding the db access before each request
private var dbVersion: String = ""
private var appVersion: String = ""
fun checkDBUpdate(configurationRepository: ConfigurationRepository, properties: ApplicationProperties) {
if ( dbVersion.isNotEmpty() && appVersion.isNotEmpty()) {
// no db access
//println("DB up-to-date, no DB access")
return
}
this.configurationRepository = configurationRepository
this.properties = properties
val dbConfig: Configuration = configurationRepository.findByConfigKey("db_version")
val applicationVersion: String = properties.getVersion()
this.dbVersion = dbConfig.configValue
this.appVersion = applicationVersion
if ( dbConfig.configValue != applicationVersion ) {
try {
val versionNumber: String = applicationVersion.replace(".", "_")
val fileName = "update_$versionNumber.sql"
val path = "./data/db/"
val file2Update = path+fileName
var sqlRows = ""
File(file2Update).forEachLine {
sqlRows += it
if ( sqlRows.contains(";") ) {
execSQL(sqlRows)
sqlRows = ""
}
}
this.connection.commit()
logger.info("Database has been updated to $applicationVersion" )
} catch (exception: Exception) {
logger.info("Database exception of $applicationVersion: " + exception.message )
this.connection.rollback()
}
}
}
fun execSQL( sql: String ) {
if (! this.initialized ) {
this.getConnection()
}
with(this.connection) {
createStatement().execute(sql)
//commit()
}
}
fun execQuery( sql: String ): ResultSet {
if (! this.initialized ) {
this.getConnection()
}
return connection.createStatement().executeQuery(sql)
}
private fun getConnection(): Connection {
val connectionProps = Properties()
connectionProps["user"] = this.properties.getDatasourceUsername()
connectionProps["password"] = this.properties.getDatasourcePassword()
this.connection = DriverManager.getConnection(this.properties.getDatasourceUrl(), connectionProps)
this.connection.autoCommit = false
this.initialized = true
return this.connection
}
}
@@ -8,7 +8,7 @@ import javax.validation.Valid
@RestController
@RequestMapping("/api")
class ExerciesController(private val exercisesRepository: ExercisesRepository) {
class ExerciseController(private val exercisesRepository: ExercisesRepository) {
@GetMapping("/exercises/{id}")
fun getExerciseById(@PathVariable(value = "id") exerciseId: Long): ResponseEntity<Exercises> {
@@ -33,7 +33,7 @@ class ExerciesController(private val exercisesRepository: ExercisesRepository) {
val updatedExercises: Exercises = existingExercises.copy(
exerciseTypeId = newExercises.exerciseTypeId,
customerId = newExercises.customerId,
datetimeExercise = newExercises.datetimeExercise,
dateAdd = newExercises.dateAdd,
quantity = newExercises.quantity,
restTime = newExercises.restTime
)
@@ -0,0 +1,35 @@
package com.aitrainer.api.controller
import com.aitrainer.api.ApiApplication
import com.aitrainer.api.repository.ConfigurationRepository
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before
import org.aspectj.lang.annotation.Pointcut
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
@Component
@Suppress("unused")
@Aspect
class ExerciseControllerAspect {
private val logger = LoggerFactory.getLogger(ApiApplication::class.simpleName)
@Autowired
private lateinit var configurationRepository: ConfigurationRepository
@Autowired
private lateinit var properties: ApplicationProperties
@Suppress("unused")
@Pointcut("execution(* com.aitrainer.api.controller.ExerciseController.*())")
fun exerciseControllerAspect() {
}
@Before("exerciseControllerAspect()")
fun loggingAop() {
Singleton.checkDBUpdate(configurationRepository, properties)
}
}
@@ -0,0 +1,34 @@
package com.aitrainer.api.controller
import com.aitrainer.api.ApiApplication
import com.aitrainer.api.repository.ConfigurationRepository
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before
import org.aspectj.lang.annotation.Pointcut
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
@Suppress("unused")
@Aspect
@Component
class ExerciseTypeControllerAspect {
private val logger = LoggerFactory.getLogger(ApiApplication::class.simpleName)
@Autowired
private lateinit var configurationRepository: ConfigurationRepository
@Autowired
private lateinit var properties: ApplicationProperties
@Suppress("unused")
@Pointcut("execution(* com.aitrainer.api.controller.ExerciseTypeController.*())")
fun exerciseTypeControllerAspect() {
}
@Before("exerciseTypeControllerAspect()")
fun loggingAop() {
Singleton.checkDBUpdate(configurationRepository, properties)
}
}
@@ -0,0 +1,21 @@
package com.aitrainer.api.model
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id
import javax.validation.constraints.NotBlank
import javax.validation.constraints.Null
@Entity
data class Configuration (
@get: NotBlank var configKey: String = "",
@get: NotBlank var configValue: String = "",
var dateAdd: String,
var dateChange: String = "",
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val configuration_id: Long = 0
)
@@ -16,6 +16,10 @@ data class Customer (
var age: Int = 0,
var sex: String = "m",
var active: String = "N",
var dateAdd: String? = null,
var dateChange: String? = null,
var dataPolicyAllowed: Int = 0,
var admin: Int = 0,
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val customer_id: Long = 0
@@ -0,0 +1,21 @@
package com.aitrainer.api.model
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id
import javax.validation.constraints.NotBlank
@Entity
class CustomerInformation (
@get: NotBlank var title: String = "",
var description: String = "",
var dateAdd: String? = null,
var displayBegin: String? = null,
var displayEnd: String? = null,
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val customerInformationId: Long = 0
)
@@ -11,9 +11,10 @@ import javax.validation.constraints.Null
data class Exercises (
@get: NonNull var exerciseTypeId: Long = 0,
@get: NonNull var customerId: Long = 0,
@get: NonNull var datetimeExercise: String? = null,
@get: NonNull var dateAdd: String? = null,
@get: NonNull var quantity: Int = 0,
@get: Null var restTime: Int?, // in seconds
@get: NonNull var unit: String? = null,
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val exerciseId: Long = 0
@@ -0,0 +1,10 @@
package com.aitrainer.api.repository
import com.aitrainer.api.model.Configuration
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface ConfigurationRepository : JpaRepository<Configuration, Long> {
fun findByConfigKey( key: String ):Configuration
}
@@ -0,0 +1,11 @@
package com.aitrainer.api.repository
import com.aitrainer.api.model.CustomerInformation
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface CustomerInformationRepository: JpaRepository<CustomerInformation, Long> {
fun findByDisplayBeginLessThanAndDisplayEndGreaterThan( dateTimeBegin: String, dateTimeEnd: String ):
List<CustomerInformation>
}
+4 -1
View File
@@ -11,5 +11,8 @@ spring.datasource.password = andio2009
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
logging.config=classpath:logback-spring.xml
logging.file=logs
logging.file=logs
application.version=0.0.2
@@ -1,49 +0,0 @@
package com.aitrainer.api.test
import org.springframework.beans.factory.annotation.Autowired
import java.sql.Connection
import java.sql.DriverManager
import java.util.*
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class AitrainerDBTest(configuration: ApplicationConfiguration) {
@Autowired
private lateinit var username: String
private lateinit var password: String
private lateinit var datasourceUrl: String
private lateinit var conn: Connection
private val conf: ApplicationConfiguration = configuration
fun initDB() {
this.username = this.conf.username.orEmpty()
this.password = this.conf.password.orEmpty()
this.datasourceUrl = this.conf.url.orEmpty()
assertTrue ("username should not be empty") { this.username.isNotEmpty() }
assertTrue ("password should not be empty") { this.password.isNotEmpty() }
assertTrue ("url should not be empty") { this.datasourceUrl.isNotEmpty() }
this.getConnection()
assertNotNull({this.conn}, "MySQL connection should not be null")
}
/**
* This method makes a connection to MySQL Server
* In this example, MySQL Server is running in the local host (so 127.0.0.1)
* at the standard port 3306
*/
private fun getConnection() {
val connectionProps = Properties()
connectionProps["user"] = this.username
connectionProps["password"] = this.password
Class.forName("com.mysql.cj.jdbc.Driver").getDeclaredConstructor().newInstance()
this.conn = DriverManager.getConnection(this.datasourceUrl, connectionProps)
}
}
@@ -1,12 +0,0 @@
package com.aitrainer.api.test
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.stereotype.Component
@Component
@ConfigurationProperties(prefix = "spring.datasource")
class ApplicationConfiguration {
var username: String? = null
var password: String? = null
var url: String? = null
}
@@ -0,0 +1,34 @@
package com.aitrainer.api.test
import com.aitrainer.api.controller.ApplicationProperties
import com.aitrainer.api.controller.Singleton
import com.aitrainer.api.repository.ConfigurationRepository
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import java.sql.ResultSet
import kotlin.test.assertEquals
@SpringBootTest
class ApplicationStartTest {
@Autowired
private lateinit var configurationRepository: ConfigurationRepository
@Autowired
private lateinit var properties: ApplicationProperties
@Test
fun testDBCheck() {
Singleton.checkDBUpdate(this.configurationRepository, this.properties)
var foundTable = false
val rs: ResultSet = Singleton.execQuery("Show tables;")
while ( rs.next() ) {
if ( rs.getString("tables_in_aitrainer") == "customer_information" ) {
foundTable = true
}
}
assertEquals(foundTable, true)
}
}
@@ -0,0 +1,31 @@
package com.aitrainer.api.test
import com.aitrainer.api.controller.ConfigurationController
import com.aitrainer.api.model.Configuration
import com.aitrainer.api.repository.ConfigurationRepository
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import kotlin.test.assertEquals
@SpringBootTest
class ConfigurationTest {
@Autowired
private lateinit var configurationRepository: ConfigurationRepository
private lateinit var configurationController: ConfigurationController
@Test
fun testUpdateConfig() {
val config: Configuration = configurationRepository.findByConfigKey("db_version")
config.configValue = "0.0.2"
configurationController = ConfigurationController(configurationRepository)
val updatedConfig: Configuration = configurationController.updateConfiguration(config)
assertEquals(updatedConfig.configValue, "0.0.2")
val foundConfig: Configuration = configurationRepository.findByConfigKey("db_version")
assertEquals(foundConfig.configValue, "0.0.2")
}
}
@@ -0,0 +1,24 @@
package com.aitrainer.api.test
import com.aitrainer.api.model.CustomerInformation
import com.aitrainer.api.repository.CustomerInformationRepository
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import kotlin.test.assertEquals
@SpringBootTest
class CustomerInformationTest {
@Autowired
private lateinit var customerInformationRepository: CustomerInformationRepository
@Test
fun getActiveCustomerInformation() {
val dateTime = "2020-06-01 09:00:00"
val info: List<CustomerInformation> = this.customerInformationRepository.
findByDisplayBeginLessThanAndDisplayEndGreaterThan(dateTime, dateTime )
assertEquals(info.size, 2)
assertEquals(info.first().title, "Fekvőtámasz világcsúcs")
}
}
@@ -33,7 +33,8 @@ class ExerciseTest {
exerciseTypeId = 3,
customerId = 11,
quantity = 100,
datetimeExercise = "2020-05-13 04:32:00",
dateAdd = "2020-05-13 04:32:00",
unit = "repeat",
restTime = null
)
val exerciseNew = exerciseRepository.save(exercise)
@@ -0,0 +1,27 @@
package com.aitrainer.api.test
import com.aitrainer.api.controller.ApplicationProperties
import org.springframework.boot.test.context.SpringBootTest
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import kotlin.test.assertEquals
@SpringBootTest
class PropertiesTest {
@Autowired
private lateinit var properties: ApplicationProperties
@Test
fun testProperties() {
val version: String = properties.getVersion()
assertEquals(version, "0.0.2")
}
@Test
fun testDatasourceUrl() {
val url: String = properties.getDatasourceUrl()
assertEquals(url, "jdbc:mysql://localhost:3306/aitrainer?serverTimezone=CET&useSSL=false&characterEncoding=UTF-8&allowMultiQueries=true")
}
}