Refactoring + tests

This commit is contained in:
Sebastien Deleuze
2016-03-19 12:18:44 +01:00
parent 73663d16cc
commit 08f9760af4
10 changed files with 170 additions and 107 deletions

View File

@@ -1,19 +1,40 @@
package io.spring.messenger
import com.fasterxml.jackson.module.kotlin.KotlinModule
import io.spring.messenger.domain.Message
import io.spring.messenger.domain.User
import io.spring.messenger.repository.MessageRepository
import io.spring.messenger.repository.UserRepository
import org.jetbrains.exposed.sql.Database
import org.postgis.geojson.PostGISModule
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.context.annotation.Bean
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder
import javax.sql.DataSource
@SpringBootApplication
open class Application {
@Bean
open fun objectMapperBuilder(): Jackson2ObjectMapperBuilder
@Bean open fun objectMapperBuilder(): Jackson2ObjectMapperBuilder
= Jackson2ObjectMapperBuilder().modulesToInstall(PostGISModule(), KotlinModule())
@Bean open fun db(dataSource: DataSource) = Database.connect(dataSource)
@Bean open fun init(db: Database, ur: UserRepository, mr: MessageRepository) = CommandLineRunner {
ur.createTable()
mr.createTable()
mr.deleteAll()
ur.deleteAll()
ur.create(User("swhite", "Skyler", "White"))
ur.create(User("jpinkman", "Jesse", "Pinkman"))
ur.create(User("wwhite", "Walter", "White"))
ur.create(User("sgoodman", "Saul", "Goodman"))
mr.create(Message("This is a test!", "swhite"))
}
}
fun main(args: Array<String>) {

View File

@@ -0,0 +1,42 @@
package io.spring.messenger
import org.jetbrains.exposed.sql.*
import org.postgis.PGbox2d
import org.postgis.PGgeometry
import org.postgis.Point
object Messages : Table() {
val id = integer("id").autoIncrement().primaryKey()
val content = text("content")
val author = reference("author", Users.userName)
val location = point("location").nullable()
}
object Users : Table() {
val userName = text("user_name").primaryKey()
val firstName = text("first_name")
val lastName = text("last_name")
val location = point("location").nullable()
}
fun Table.point(name: String, srid: Int = 4326): Column<Point> = registerColumn(name, PointColumnType())
infix fun ExpressionWithColumnType<*>.within(box: PGbox2d) : Op<Boolean> = WithinOp(this, box)
private class PointColumnType(val srid: Int = 4326): ColumnType() {
override fun sqlType() = "GEOMETRY(Point, $srid)"
override fun valueFromDB(value: Any) = if (value is PGgeometry) value.geometry else value
override fun notNullValueToDB(value: Any): Any {
if (value is Point) {
if (value.srid == Point.UNKNOWN_SRID) value.srid = srid
return PGgeometry(value)
}
return value
}
}
private class WithinOp(val expr1: Expression<*>, val box: PGbox2d) : Op<Boolean>() {
override fun toSQL(queryBuilder: QueryBuilder) =
"${expr1.toSQL(queryBuilder)} && ST_MakeEnvelope(${box.llb.x}, ${box.llb.y}, ${box.urt.x}, ${box.urt.y}, 4326)"
}

View File

@@ -1,42 +0,0 @@
package io.spring.messenger.config
import io.spring.messenger.domain.Message
import io.spring.messenger.domain.User
import io.spring.messenger.repository.*
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.exists
import org.springframework.boot.CommandLineRunner
import javax.sql.DataSource
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
open class DatabaseConfig {
@Bean
open fun db(dataSource: DataSource) = Database.connect(dataSource)
@Bean
open fun init(db: Database, userRepository: UserRepository, messageRepository: MessageRepository) = CommandLineRunner {
val swhite = User("swhite", "Skyler", "White")
val jpinkman = User("jpinkman", "Jesse", "Pinkman")
val walter = User("wwhite", "Walter", "White")
val sgoodman = User("sgoodman", "Saul", "Goodman")
db.transaction {
if(!Users.exists()) {
create(Users)
userRepository.create(swhite)
userRepository.create(jpinkman)
userRepository.create(walter)
userRepository.create(sgoodman)
}
if(!Messages.exists()) {
create(Messages)
messageRepository.create(Message("This is a test!", swhite.userName))
}
}
}
}

View File

@@ -1,6 +1,8 @@
package io.spring.messenger.repository
import io.spring.messenger.Messages
import io.spring.messenger.domain.Message
import io.spring.messenger.within
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.statements.UpdateBuilder
import org.postgis.PGbox2d
@@ -10,6 +12,10 @@ import org.springframework.stereotype.Repository
@Repository
open class MessageRepository @Autowired constructor(val db: Database) {
open fun createTable() = db.transaction {
create(Messages)
}
open fun create(m: Message) = db.transaction {
m.id = Messages.insert(map(m)).get(Messages.id)
m
@@ -23,6 +29,10 @@ open class MessageRepository @Autowired constructor(val db: Database) {
unmap(Messages.select { Messages.location within box })
}
open fun deleteAll() = db.transaction {
Messages.deleteAll()
}
private fun map(m: Message): Messages.(UpdateBuilder<*>) -> Unit = {
if (m.id != null) it[id] = m.id
it[content] = m.content

View File

@@ -1,39 +0,0 @@
package io.spring.messenger.repository
import org.jetbrains.exposed.sql.*
import org.postgis.PGbox2d
import org.postgis.PGgeometry
import org.postgis.Point
fun Table.point(name: String, srid: Int = 4326): Column<Point> = registerColumn(name, PointColumnType())
class PointColumnType(val srid: Int = 4326): ColumnType() {
override fun sqlType(): String = "GEOMETRY(Point, $srid)"
override fun valueFromDB(value: Any): Any {
if (value is PGgeometry) {
return value.geometry
}
return value
}
override fun notNullValueToDB(value: Any): Any {
if (value is Point) {
if (value.srid == Point.UNKNOWN_SRID) value.srid = srid
return PGgeometry(value)
}
return value
}
}
infix fun ExpressionWithColumnType<*>.within(box: PGbox2d) : Op<Boolean> {
return WithinOp(this, box)
}
class WithinOp(val expr1: Expression<*>, val box: PGbox2d) : Op<Boolean>() {
override fun toSQL(queryBuilder: QueryBuilder) = """${expr1.toSQL(queryBuilder)}
&& ST_MakeEnvelope(${box.llb.x}, ${box.llb.y}, ${box.urt.x}, ${box.urt.y}, 4326)"""
}

View File

@@ -1,17 +0,0 @@
package io.spring.messenger.repository
import org.jetbrains.exposed.sql.Table
object Messages : Table() {
val id = integer("id").autoIncrement().primaryKey()
val content = text("content")
val author = reference("author", Users.userName)
val location = point("location").nullable()
}
object Users : Table() {
val userName = text("user_name").primaryKey()
val firstName = text("first_name")
val lastName = text("last_name")
val location = point("location").nullable()
}

View File

@@ -1,6 +1,8 @@
package io.spring.messenger.repository
import io.spring.messenger.Users
import io.spring.messenger.domain.User
import io.spring.messenger.within
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.statements.UpdateBuilder
import org.postgis.PGbox2d
@@ -11,15 +13,19 @@ import org.springframework.stereotype.Repository
@Repository
open class UserRepository @Autowired constructor(val db: Database) {
open fun updateLocation(userName:String, location: Point) = db.transaction {
location.srid = 4326
Users.update({Users.userName eq userName}) { it[Users.location] = location}
open fun createTable() = db.transaction {
create(Users)
}
open fun create(user: User) = db.transaction {
Users.insert( map(user) )
}
open fun updateLocation(userName:String, location: Point) = db.transaction {
location.srid = 4326
Users.update({Users.userName eq userName}) { it[Users.location] = location}
}
open fun findAll() = db.transaction {
unmap(Users.selectAll())
}
@@ -28,6 +34,10 @@ open class UserRepository @Autowired constructor(val db: Database) {
unmap(Users.select { Users.location within box })
}
open fun deleteAll() = db.transaction {
Users.deleteAll()
}
private fun map(u: User): Users.(UpdateBuilder<*>) -> Unit = {
it[userName] = u.userName
it[firstName] = u.firstName

View File

@@ -11,11 +11,9 @@ import org.springframework.web.bind.annotation.*
@RestController @RequestMapping("/user")
class UserController @Autowired constructor(val repository: UserRepository) {
@GetMapping
fun findAll() = repository.findAll()
@GetMapping fun findAll() = repository.findAll()
@PostMapping
fun create(@RequestBody u: User) = repository.create(u)
@PostMapping fun create(@RequestBody u: User) = repository.create(u)
@PutMapping("/{userName}/location/{x},{y}") @ResponseStatus(NO_CONTENT)
fun updateLocation(@PathVariable userName:String, @PathVariable x: Double, @PathVariable y: Double)

View File

@@ -0,0 +1,40 @@
package io.spring.messenger
import io.spring.messenger.repository.MessageRepository
import io.spring.messenger.repository.UserRepository
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.http.MediaType.APPLICATION_JSON
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.SpringApplicationConfiguration
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner
import org.springframework.test.context.web.WebAppConfiguration
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.setup.MockMvcBuilders
import org.springframework.web.context.WebApplicationContext
@RunWith(SpringJUnit4ClassRunner::class)
@SpringApplicationConfiguration(classes = arrayOf(Application::class))
@WebAppConfiguration
class MessageControllerTests {
@Autowired lateinit var context: WebApplicationContext
@Autowired lateinit var userRepository: UserRepository
@Autowired lateinit var messageRepository: MessageRepository
lateinit var mockMvc: MockMvc
@Before fun setUp() {
messageRepository.deleteAll()
userRepository.deleteAll()
mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build()
}
@Test fun findUsers() {
mockMvc.perform(get("/message").accept(APPLICATION_JSON)).andExpect(status().isOk)
}
}

View File

@@ -0,0 +1,40 @@
package io.spring.messenger
import io.spring.messenger.repository.MessageRepository
import io.spring.messenger.repository.UserRepository
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.http.MediaType.APPLICATION_JSON
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.SpringApplicationConfiguration
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner
import org.springframework.test.context.web.WebAppConfiguration
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.setup.MockMvcBuilders
import org.springframework.web.context.WebApplicationContext
@RunWith(SpringJUnit4ClassRunner::class)
@SpringApplicationConfiguration(classes = arrayOf(Application::class))
@WebAppConfiguration
class UserControllerTests {
@Autowired lateinit var context: WebApplicationContext
@Autowired lateinit var userRepository: UserRepository
@Autowired lateinit var messageRepository: MessageRepository
lateinit var mockMvc: MockMvc
@Before fun setUp() {
messageRepository.deleteAll()
userRepository.deleteAll()
mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build()
}
@Test fun findUsers() {
mockMvc.perform(get("/user").accept(APPLICATION_JSON)).andExpect(status().isOk)
}
}