initial cut of backend/web modules
This commit is contained in:
parent
072d39d101
commit
d94534a2b8
20 changed files with 196 additions and 38 deletions
1
backend/.gitignore
vendored
Normal file
1
backend/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/build
|
21
backend/build.gradle.kts
Normal file
21
backend/build.gradle.kts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
plugins {
|
||||||
|
id("kotlin-platform-jvm")
|
||||||
|
application
|
||||||
|
kotlin("plugin.serialization")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:${Versions.kotlin}")
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.kotlinCoroutines}")
|
||||||
|
|
||||||
|
implementation("io.ktor:ktor-server-core:${Versions.ktor}")
|
||||||
|
implementation("io.ktor:ktor-server-netty:${Versions.ktor}")
|
||||||
|
implementation("io.ktor:ktor-serialization:${Versions.ktor}")
|
||||||
|
|
||||||
|
implementation("ch.qos.logback:logback-classic:1.2.3")
|
||||||
|
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:${Versions.kotlinxSerialization}") // JVM dependency
|
||||||
|
implementation("io.ktor:ktor-websockets:${Versions.ktor}")
|
||||||
|
|
||||||
|
implementation(project(":common"))
|
||||||
|
}
|
|
@ -1,23 +1,17 @@
|
||||||
package com.surrus
|
|
||||||
|
|
||||||
import io.ktor.response.*
|
import io.ktor.response.*
|
||||||
import io.ktor.routing.*
|
import io.ktor.routing.*
|
||||||
import io.ktor.serialization.*
|
import io.ktor.serialization.*
|
||||||
import io.ktor.server.engine.*
|
import io.ktor.server.engine.*
|
||||||
import io.ktor.server.netty.*
|
import io.ktor.server.netty.*
|
||||||
import org.litote.kmongo.*
|
|
||||||
import org.litote.kmongo.async.*
|
|
||||||
import org.litote.kmongo.coroutine.*
|
|
||||||
import org.litote.kmongo.async.getCollection
|
|
||||||
import com.mongodb.ConnectionString
|
|
||||||
import com.surrus.common.repository.PeopleInSpaceRepository
|
import com.surrus.common.repository.PeopleInSpaceRepository
|
||||||
import io.ktor.application.call
|
import io.ktor.application.call
|
||||||
import io.ktor.application.install
|
import io.ktor.application.install
|
||||||
import io.ktor.features.ContentNegotiation
|
import io.ktor.features.ContentNegotiation
|
||||||
import kotlinx.coroutines.GlobalScope
|
import io.ktor.http.ContentType
|
||||||
|
import io.ktor.http.content.resources
|
||||||
|
import io.ktor.http.content.static
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import javax.xml.bind.JAXBElement
|
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
val repository = PeopleInSpaceRepository()
|
val repository = PeopleInSpaceRepository()
|
||||||
|
@ -28,8 +22,20 @@ fun main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
routing {
|
routing {
|
||||||
|
|
||||||
|
get("/") {
|
||||||
|
call.respondText(
|
||||||
|
this::class.java.classLoader.getResource("index.html")!!.readText(),
|
||||||
|
ContentType.Text.Html
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
static("/") {
|
||||||
|
resources("")
|
||||||
|
}
|
||||||
|
|
||||||
get("/people") {
|
get("/people") {
|
||||||
repository.fetchPeopleAsFlow().collect {
|
repository.fetchPeopleAsFlow()?.collect {
|
||||||
call.respond(it)
|
call.respond(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
11
backend/src/main/resources/index.html
Normal file
11
backend/src/main/resources/index.html
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>People In Space</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>People In Space</h1>
|
||||||
|
<div id="root"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -19,10 +19,8 @@ allprojects {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
jcenter()
|
jcenter()
|
||||||
|
maven("https://kotlin.bintray.com/kotlin-js-wrappers/")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register("clean").configure {
|
|
||||||
delete("build")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ android {
|
||||||
compileSdkVersion(29)
|
compileSdkVersion(29)
|
||||||
buildToolsVersion("29.0.2")
|
buildToolsVersion("29.0.2")
|
||||||
|
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion(21)
|
minSdkVersion(21)
|
||||||
targetSdkVersion(29)
|
targetSdkVersion(29)
|
||||||
|
@ -57,6 +56,11 @@ kotlin {
|
||||||
homepage = "Link to a Kotlin/Native module homepage"
|
homepage = "Link to a Kotlin/Native module homepage"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
js {
|
||||||
|
browser {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
val commonMain by getting {
|
val commonMain by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -123,9 +127,6 @@ kotlin {
|
||||||
|
|
||||||
// Ktor
|
// Ktor
|
||||||
implementation("io.ktor:ktor-server-core:${Versions.ktor}")
|
implementation("io.ktor:ktor-server-core:${Versions.ktor}")
|
||||||
implementation("io.ktor:ktor-server-netty:${Versions.ktor}")
|
|
||||||
implementation("io.ktor:ktor-websockets:${Versions.ktor}")
|
|
||||||
implementation("org.litote.kmongo:kmongo-coroutine-serialization:3.12.2")
|
|
||||||
|
|
||||||
implementation("io.ktor:ktor-client-core-jvm:${Versions.ktor}")
|
implementation("io.ktor:ktor-client-core-jvm:${Versions.ktor}")
|
||||||
implementation("io.ktor:ktor-client-json-jvm:${Versions.ktor}")
|
implementation("io.ktor:ktor-client-json-jvm:${Versions.ktor}")
|
||||||
|
@ -206,6 +207,27 @@ kotlin {
|
||||||
implementation("com.squareup.sqldelight:runtime-macosx64:${Versions.sqlDelight}")
|
implementation("com.squareup.sqldelight:runtime-macosx64:${Versions.sqlDelight}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val jsMain by getting {
|
||||||
|
dependencies {
|
||||||
|
implementation(kotlin("stdlib-js"))
|
||||||
|
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:${Versions.kotlinCoroutines}")
|
||||||
|
|
||||||
|
// ktor
|
||||||
|
implementation("io.ktor:ktor-client-js:${Versions.ktor}") //include http&websockets
|
||||||
|
implementation("io.ktor:ktor-client-json-js:${Versions.ktor}")
|
||||||
|
implementation("io.ktor:ktor-client-logging-js:${Versions.ktor}")
|
||||||
|
implementation("io.ktor:ktor-client-serialization-js:${Versions.ktor}")
|
||||||
|
|
||||||
|
// Serialize
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:${Versions.kotlinxSerialization}")
|
||||||
|
|
||||||
|
// SQL Delight
|
||||||
|
//implementation("com.squareup.sqldelight:sqljs-driver:${Versions.sqlDelight}")
|
||||||
|
implementation("com.squareup.sqldelight:runtime-js:${Versions.sqlDelight}")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,3 +237,4 @@ sqldelight {
|
||||||
sourceFolders = listOf("sqldelight")
|
sourceFolders = listOf("sqldelight")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import kotlinx.coroutines.launch
|
||||||
|
|
||||||
lateinit var appContext: Context
|
lateinit var appContext: Context
|
||||||
|
|
||||||
actual fun createDb(): PeopleInSpaceDatabase {
|
actual fun createDb(): PeopleInSpaceDatabase? {
|
||||||
val driver = AndroidSqliteDriver(PeopleInSpaceDatabase.Schema, appContext, "peopleinspace.db")
|
val driver = AndroidSqliteDriver(PeopleInSpaceDatabase.Schema, appContext, "peopleinspace.db")
|
||||||
return PeopleInSpaceDatabase(driver)
|
return PeopleInSpaceDatabase(driver)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import com.surrus.peopleinspace.db.PeopleInSpaceDatabase
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
|
|
||||||
expect fun createDb() : PeopleInSpaceDatabase
|
expect fun createDb() : PeopleInSpaceDatabase?
|
||||||
|
|
||||||
// TEMP until following is resolved https://github.com/ktorio/ktor/issues/1622
|
// TEMP until following is resolved https://github.com/ktorio/ktor/issues/1622
|
||||||
expect fun ktorScope(block: suspend () -> Unit)
|
expect fun ktorScope(block: suspend () -> Unit)
|
||||||
|
@ -18,7 +18,7 @@ expect fun ktorScope(block: suspend () -> Unit)
|
||||||
class PeopleInSpaceRepository {
|
class PeopleInSpaceRepository {
|
||||||
private val peopleInSpaceApi = PeopleInSpaceApi()
|
private val peopleInSpaceApi = PeopleInSpaceApi()
|
||||||
private val peopleInSpaceDatabase = createDb()
|
private val peopleInSpaceDatabase = createDb()
|
||||||
private val peopleInSpaceQueries = peopleInSpaceDatabase.peopleInSpaceQueries
|
private val peopleInSpaceQueries = peopleInSpaceDatabase?.peopleInSpaceQueries
|
||||||
|
|
||||||
init {
|
init {
|
||||||
ktorScope {
|
ktorScope {
|
||||||
|
@ -26,25 +26,25 @@ class PeopleInSpaceRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fetchPeopleAsFlow() = peopleInSpaceQueries.selectAll(mapper = { name, craft ->
|
fun fetchPeopleAsFlow() = peopleInSpaceQueries?.selectAll(mapper = { name, craft ->
|
||||||
Assignment(name = name, craft = craft)
|
Assignment(name = name, craft = craft)
|
||||||
}).asFlow().mapToList()
|
})?.asFlow()?.mapToList()
|
||||||
|
|
||||||
private suspend fun fetchAndStorePeople() {
|
private suspend fun fetchAndStorePeople() {
|
||||||
val result = peopleInSpaceApi.fetchPeople()
|
val result = peopleInSpaceApi.fetchPeople()
|
||||||
|
|
||||||
// this is very basic implementation for now that removes all existing rows
|
// this is very basic implementation for now that removes all existing rows
|
||||||
// in db and then inserts reults from api request
|
// in db and then inserts reults from api request
|
||||||
peopleInSpaceQueries.deleteAll()
|
peopleInSpaceQueries?.deleteAll()
|
||||||
result.people.forEach {
|
result.people.forEach {
|
||||||
peopleInSpaceQueries.insertItem(it.name, it.craft)
|
peopleInSpaceQueries?.insertItem(it.name, it.craft)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// called from iOS/watchOS/macOS client
|
// called from iOS/watchOS/macOS client
|
||||||
fun fetchPeople(success: (List<Assignment>) -> Unit) {
|
fun fetchPeople(success: (List<Assignment>) -> Unit) {
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
GlobalScope.launch(Dispatchers.Main) {
|
||||||
fetchPeopleAsFlow().collect {
|
fetchPeopleAsFlow()?.collect {
|
||||||
success(it)
|
success(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
actual fun createDb(): PeopleInSpaceDatabase {
|
actual fun createDb(): PeopleInSpaceDatabase? {
|
||||||
val driver = NativeSqliteDriver(PeopleInSpaceDatabase.Schema, "peopleinspace.db")
|
val driver = NativeSqliteDriver(PeopleInSpaceDatabase.Schema, "peopleinspace.db")
|
||||||
return PeopleInSpaceDatabase(driver)
|
return PeopleInSpaceDatabase(driver)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.surrus.common.repository
|
||||||
|
|
||||||
|
import com.surrus.peopleinspace.db.PeopleInSpaceDatabase
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
|
actual fun createDb(): PeopleInSpaceDatabase? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun ktorScope(block: suspend () -> Unit) {
|
||||||
|
GlobalScope.launch(Dispatchers.Main) { block() }
|
||||||
|
}
|
|
@ -7,12 +7,14 @@ import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
@InternalCoroutinesApi
|
@InternalCoroutinesApi
|
||||||
fun main() = runBlocking {
|
fun main() {
|
||||||
|
runBlocking {
|
||||||
val api = PeopleInSpaceApi()
|
val api = PeopleInSpaceApi()
|
||||||
println(api.fetchPeople())
|
println(api.fetchPeople())
|
||||||
|
|
||||||
val repository = PeopleInSpaceRepository()
|
val repository = PeopleInSpaceRepository()
|
||||||
repository.fetchPeopleAsFlow().collect {
|
repository.fetchPeopleAsFlow()?.collect {
|
||||||
println(it)
|
println(it)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -4,7 +4,7 @@ import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver
|
||||||
import com.surrus.peopleinspace.db.PeopleInSpaceDatabase
|
import com.surrus.peopleinspace.db.PeopleInSpaceDatabase
|
||||||
|
|
||||||
|
|
||||||
actual fun createDb(): PeopleInSpaceDatabase {
|
actual fun createDb(): PeopleInSpaceDatabase? {
|
||||||
val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY)
|
val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY)
|
||||||
.also { PeopleInSpaceDatabase.Schema.create(it) }
|
.also { PeopleInSpaceDatabase.Schema.create(it) }
|
||||||
return PeopleInSpaceDatabase(driver)
|
return PeopleInSpaceDatabase(driver)
|
||||||
|
|
|
@ -3,7 +3,7 @@ package com.surrus.common.repository
|
||||||
import com.squareup.sqldelight.drivers.native.NativeSqliteDriver
|
import com.squareup.sqldelight.drivers.native.NativeSqliteDriver
|
||||||
import com.surrus.peopleinspace.db.PeopleInSpaceDatabase
|
import com.surrus.peopleinspace.db.PeopleInSpaceDatabase
|
||||||
|
|
||||||
actual fun createDb(): PeopleInSpaceDatabase {
|
actual fun createDb(): PeopleInSpaceDatabase? {
|
||||||
val driver = NativeSqliteDriver(PeopleInSpaceDatabase.Schema, "peopleinspace.db")
|
val driver = NativeSqliteDriver(PeopleInSpaceDatabase.Schema, "peopleinspace.db")
|
||||||
return PeopleInSpaceDatabase(driver)
|
return PeopleInSpaceDatabase(driver)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
actual fun createDb(): PeopleInSpaceDatabase {
|
actual fun createDb(): PeopleInSpaceDatabase? {
|
||||||
val driver = NativeSqliteDriver(PeopleInSpaceDatabase.Schema, "peopleinspace.db")
|
val driver = NativeSqliteDriver(PeopleInSpaceDatabase.Schema, "peopleinspace.db")
|
||||||
return PeopleInSpaceDatabase(driver)
|
return PeopleInSpaceDatabase(driver)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
include(":web")
|
||||||
|
include(":backend")
|
||||||
rootProject.name = "PeopleInSpace"
|
rootProject.name = "PeopleInSpace"
|
||||||
|
|
||||||
enableFeaturePreview("GRADLE_METADATA")
|
enableFeaturePreview("GRADLE_METADATA")
|
||||||
|
|
1
web/.gitignore
vendored
Normal file
1
web/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/build
|
29
web/build.gradle.kts
Normal file
29
web/build.gradle.kts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
plugins {
|
||||||
|
kotlin("js")
|
||||||
|
kotlin("plugin.serialization")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(kotlin("stdlib-js"))
|
||||||
|
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:1.3.5")
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-html-js:0.7.1")
|
||||||
|
implementation(npm("text-encoding"))
|
||||||
|
implementation(npm("abort-controller"))
|
||||||
|
implementation(npm("bufferutil"))
|
||||||
|
implementation(npm("utf-8-validate"))
|
||||||
|
implementation(npm("fs"))
|
||||||
|
|
||||||
|
//React, React DOM + Wrappers (chapter 3)
|
||||||
|
implementation("org.jetbrains:kotlin-react:16.13.0-pre.93-kotlin-1.3.70")
|
||||||
|
implementation("org.jetbrains:kotlin-react-dom:16.13.0-pre.93-kotlin-1.3.70")
|
||||||
|
implementation(npm("react", "16.13.0"))
|
||||||
|
implementation(npm("react-dom", "16.13.0"))
|
||||||
|
|
||||||
|
|
||||||
|
implementation(project(":common"))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
kotlin.target.browser { }
|
||||||
|
|
29
web/src/main/kotlin/App.kt
Normal file
29
web/src/main/kotlin/App.kt
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import com.surrus.common.remote.Assignment
|
||||||
|
import com.surrus.common.remote.PeopleInSpaceApi
|
||||||
|
import react.*
|
||||||
|
import react.dom.*
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
|
||||||
|
val scope = MainScope()
|
||||||
|
val api = PeopleInSpaceApi()
|
||||||
|
|
||||||
|
val App = functionalComponent<RProps> { _ ->
|
||||||
|
val (people, setPeople) = useState(emptyList<Assignment>())
|
||||||
|
|
||||||
|
useEffect(dependencies = listOf()) {
|
||||||
|
scope.launch {
|
||||||
|
setPeople(api.fetchPeople().people)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
+"People In Space"
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
people.forEach { item ->
|
||||||
|
li {
|
||||||
|
+"${item.name} (${item.craft})"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
web/src/main/kotlin/Main.kt
Normal file
9
web/src/main/kotlin/Main.kt
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import react.child
|
||||||
|
import react.dom.render
|
||||||
|
import kotlin.browser.document
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
render(document.getElementById("root")) {
|
||||||
|
child(functionalComponent = App)
|
||||||
|
}
|
||||||
|
}
|
11
web/src/main/resources/index.html
Normal file
11
web/src/main/resources/index.html
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>People In Space</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script src="web.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue