diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts new file mode 100644 index 0000000..1e5ea2a --- /dev/null +++ b/backend/build.gradle.kts @@ -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")) +} diff --git a/common/src/jvmMain/kotlin/com/surrus/Server.kt b/backend/src/main/kotlin/Server.kt similarity index 59% rename from common/src/jvmMain/kotlin/com/surrus/Server.kt rename to backend/src/main/kotlin/Server.kt index 222a759..b03e788 100644 --- a/common/src/jvmMain/kotlin/com/surrus/Server.kt +++ b/backend/src/main/kotlin/Server.kt @@ -1,23 +1,17 @@ -package com.surrus - import io.ktor.response.* import io.ktor.routing.* import io.ktor.serialization.* import io.ktor.server.engine.* 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 io.ktor.application.call import io.ktor.application.install 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.launch -import javax.xml.bind.JAXBElement + fun main() { val repository = PeopleInSpaceRepository() @@ -28,11 +22,23 @@ fun main() { } routing { + + get("/") { + call.respondText( + this::class.java.classLoader.getResource("index.html")!!.readText(), + ContentType.Text.Html + ) + } + + static("/") { + resources("") + } + get("/people") { - repository.fetchPeopleAsFlow().collect { + repository.fetchPeopleAsFlow()?.collect { call.respond(it) } } } }.start(wait = true) -} \ No newline at end of file +} diff --git a/backend/src/main/resources/index.html b/backend/src/main/resources/index.html new file mode 100644 index 0000000..2112171 --- /dev/null +++ b/backend/src/main/resources/index.html @@ -0,0 +1,11 @@ + + + + + People In Space + + +

People In Space

+
+ + diff --git a/build.gradle.kts b/build.gradle.kts index 5ff4c07..011305d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,10 +19,8 @@ allprojects { google() mavenCentral() jcenter() + maven("https://kotlin.bintray.com/kotlin-js-wrappers/") } } -tasks.register("clean").configure { - delete("build") -} diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 92f56ec..c784937 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -10,7 +10,6 @@ android { compileSdkVersion(29) buildToolsVersion("29.0.2") - defaultConfig { minSdkVersion(21) targetSdkVersion(29) @@ -57,6 +56,11 @@ kotlin { homepage = "Link to a Kotlin/Native module homepage" } + js { + browser { + } + } + sourceSets { val commonMain by getting { dependencies { @@ -123,9 +127,6 @@ kotlin { // 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-json-jvm:${Versions.ktor}") @@ -206,6 +207,27 @@ kotlin { 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}") + } + } } } @@ -214,4 +236,5 @@ sqldelight { packageName = "com.surrus.peopleinspace.db" sourceFolders = listOf("sqldelight") } -} \ No newline at end of file +} + diff --git a/common/src/androidMain/kotlin/com/surrus/common/repository/actual.kt b/common/src/androidMain/kotlin/com/surrus/common/repository/actual.kt index 41c2a36..d5556a1 100644 --- a/common/src/androidMain/kotlin/com/surrus/common/repository/actual.kt +++ b/common/src/androidMain/kotlin/com/surrus/common/repository/actual.kt @@ -10,7 +10,7 @@ import kotlinx.coroutines.launch lateinit var appContext: Context -actual fun createDb(): PeopleInSpaceDatabase { +actual fun createDb(): PeopleInSpaceDatabase? { val driver = AndroidSqliteDriver(PeopleInSpaceDatabase.Schema, appContext, "peopleinspace.db") return PeopleInSpaceDatabase(driver) } diff --git a/common/src/commonMain/kotlin/com/surrus/common/repository/PeopleInSpaceRepository.kt b/common/src/commonMain/kotlin/com/surrus/common/repository/PeopleInSpaceRepository.kt index 424e858..b7c86ff 100644 --- a/common/src/commonMain/kotlin/com/surrus/common/repository/PeopleInSpaceRepository.kt +++ b/common/src/commonMain/kotlin/com/surrus/common/repository/PeopleInSpaceRepository.kt @@ -9,7 +9,7 @@ import com.surrus.peopleinspace.db.PeopleInSpaceDatabase import kotlinx.coroutines.* 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 expect fun ktorScope(block: suspend () -> Unit) @@ -18,7 +18,7 @@ expect fun ktorScope(block: suspend () -> Unit) class PeopleInSpaceRepository { private val peopleInSpaceApi = PeopleInSpaceApi() private val peopleInSpaceDatabase = createDb() - private val peopleInSpaceQueries = peopleInSpaceDatabase.peopleInSpaceQueries + private val peopleInSpaceQueries = peopleInSpaceDatabase?.peopleInSpaceQueries init { 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) - }).asFlow().mapToList() + })?.asFlow()?.mapToList() private suspend fun fetchAndStorePeople() { val result = peopleInSpaceApi.fetchPeople() // this is very basic implementation for now that removes all existing rows // in db and then inserts reults from api request - peopleInSpaceQueries.deleteAll() + peopleInSpaceQueries?.deleteAll() result.people.forEach { - peopleInSpaceQueries.insertItem(it.name, it.craft) + peopleInSpaceQueries?.insertItem(it.name, it.craft) } } // called from iOS/watchOS/macOS client fun fetchPeople(success: (List) -> Unit) { GlobalScope.launch(Dispatchers.Main) { - fetchPeopleAsFlow().collect { + fetchPeopleAsFlow()?.collect { success(it) } } diff --git a/common/src/iOSMain/kotlin/com/surrus/common/repository/actual.kt b/common/src/iOSMain/kotlin/com/surrus/common/repository/actual.kt index 674c0e9..eba034b 100644 --- a/common/src/iOSMain/kotlin/com/surrus/common/repository/actual.kt +++ b/common/src/iOSMain/kotlin/com/surrus/common/repository/actual.kt @@ -6,7 +6,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch -actual fun createDb(): PeopleInSpaceDatabase { +actual fun createDb(): PeopleInSpaceDatabase? { val driver = NativeSqliteDriver(PeopleInSpaceDatabase.Schema, "peopleinspace.db") return PeopleInSpaceDatabase(driver) } diff --git a/common/src/jsMain/kotlin/com/surrus/common/repository/actual.kt b/common/src/jsMain/kotlin/com/surrus/common/repository/actual.kt new file mode 100644 index 0000000..8afb557 --- /dev/null +++ b/common/src/jsMain/kotlin/com/surrus/common/repository/actual.kt @@ -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() } +} diff --git a/common/src/jvmMain/kotlin/com/surrus/Main.kt b/common/src/jvmMain/kotlin/com/surrus/Main.kt index 66d6db2..311ee12 100644 --- a/common/src/jvmMain/kotlin/com/surrus/Main.kt +++ b/common/src/jvmMain/kotlin/com/surrus/Main.kt @@ -7,12 +7,14 @@ import kotlinx.coroutines.flow.collect import kotlinx.coroutines.runBlocking @InternalCoroutinesApi -fun main() = runBlocking { - val api = PeopleInSpaceApi() - println(api.fetchPeople()) +fun main() { + runBlocking { + val api = PeopleInSpaceApi() + println(api.fetchPeople()) - val repository = PeopleInSpaceRepository() - repository.fetchPeopleAsFlow().collect { - println(it) + val repository = PeopleInSpaceRepository() + repository.fetchPeopleAsFlow()?.collect { + println(it) + } } } \ No newline at end of file diff --git a/common/src/jvmMain/kotlin/com/surrus/common/repository/actual.kt b/common/src/jvmMain/kotlin/com/surrus/common/repository/actual.kt index e01d3e2..164ec04 100644 --- a/common/src/jvmMain/kotlin/com/surrus/common/repository/actual.kt +++ b/common/src/jvmMain/kotlin/com/surrus/common/repository/actual.kt @@ -4,7 +4,7 @@ import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver import com.surrus.peopleinspace.db.PeopleInSpaceDatabase -actual fun createDb(): PeopleInSpaceDatabase { +actual fun createDb(): PeopleInSpaceDatabase? { val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY) .also { PeopleInSpaceDatabase.Schema.create(it) } return PeopleInSpaceDatabase(driver) diff --git a/common/src/macOSMain/kotlin/com/surrus/common/repository/actual.kt b/common/src/macOSMain/kotlin/com/surrus/common/repository/actual.kt index 30a86c3..289d7b9 100644 --- a/common/src/macOSMain/kotlin/com/surrus/common/repository/actual.kt +++ b/common/src/macOSMain/kotlin/com/surrus/common/repository/actual.kt @@ -3,7 +3,7 @@ package com.surrus.common.repository import com.squareup.sqldelight.drivers.native.NativeSqliteDriver import com.surrus.peopleinspace.db.PeopleInSpaceDatabase -actual fun createDb(): PeopleInSpaceDatabase { +actual fun createDb(): PeopleInSpaceDatabase? { val driver = NativeSqliteDriver(PeopleInSpaceDatabase.Schema, "peopleinspace.db") return PeopleInSpaceDatabase(driver) } diff --git a/common/src/watchMain/kotlin/com/surrus/common/repository/actual.kt b/common/src/watchMain/kotlin/com/surrus/common/repository/actual.kt index 674c0e9..eba034b 100644 --- a/common/src/watchMain/kotlin/com/surrus/common/repository/actual.kt +++ b/common/src/watchMain/kotlin/com/surrus/common/repository/actual.kt @@ -6,7 +6,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch -actual fun createDb(): PeopleInSpaceDatabase { +actual fun createDb(): PeopleInSpaceDatabase? { val driver = NativeSqliteDriver(PeopleInSpaceDatabase.Schema, "peopleinspace.db") return PeopleInSpaceDatabase(driver) } diff --git a/settings.gradle.kts b/settings.gradle.kts index 31f8b1a..d087da3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,3 +1,5 @@ +include(":web") +include(":backend") rootProject.name = "PeopleInSpace" enableFeaturePreview("GRADLE_METADATA") diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/web/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/web/build.gradle.kts b/web/build.gradle.kts new file mode 100644 index 0000000..8794eaf --- /dev/null +++ b/web/build.gradle.kts @@ -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 { } + diff --git a/web/src/main/kotlin/App.kt b/web/src/main/kotlin/App.kt new file mode 100644 index 0000000..4f18d5f --- /dev/null +++ b/web/src/main/kotlin/App.kt @@ -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 { _ -> + val (people, setPeople) = useState(emptyList()) + + useEffect(dependencies = listOf()) { + scope.launch { + setPeople(api.fetchPeople().people) + } + } + + h1 { + +"People In Space" + } + ul { + people.forEach { item -> + li { + +"${item.name} (${item.craft})" + } + } + } +} diff --git a/web/src/main/kotlin/Main.kt b/web/src/main/kotlin/Main.kt new file mode 100644 index 0000000..0371cc0 --- /dev/null +++ b/web/src/main/kotlin/Main.kt @@ -0,0 +1,9 @@ +import react.child +import react.dom.render +import kotlin.browser.document + +fun main() { + render(document.getElementById("root")) { + child(functionalComponent = App) + } +} \ No newline at end of file diff --git a/web/src/main/resources/index.html b/web/src/main/resources/index.html new file mode 100644 index 0000000..20eda5b --- /dev/null +++ b/web/src/main/resources/index.html @@ -0,0 +1,11 @@ + + + + + People In Space + + +
+ + +