mirror of
https://github.com/jlengrand/geospatial-messenger.git
synced 2026-03-10 08:21:17 +00:00
Refactoring + tests
This commit is contained in:
@@ -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>) {
|
||||
|
||||
42
src/main/kotlin/io/spring/messenger/Database.kt
Normal file
42
src/main/kotlin/io/spring/messenger/Database.kt
Normal 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)"
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)"""
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
40
src/test/kotlin/io/spring/messenger/UserControllerTests.kt
Normal file
40
src/test/kotlin/io/spring/messenger/UserControllerTests.kt
Normal 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)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user