Add Routing, Content Negotiation, and kotlinx.serialization plugins. Run it!!! What do you see? Warmup Hint: if you select kotlinx.serialization at the plugins stage of the wizard, the other two will be selected automatically
Add Routing, Content Negotiation, and kotlinx.serialization plugins. Run it!!! What do you see? get("/") { call.respondText("Hello World!") } get("/json/kotlinx-serialization") { call.respond(mapOf("hello" to "world")) }
Add Routing, Content Negotiation, and kotlinx.serialization plugins. Run it!!! What do you see? https: // github.com/antonarhipov/ktor-workshop % git branch * main branch01 branch02 branch03
Add Routing, Content Negotiation, and kotlinx.serialization plugins. Run it!!! What do you see? https: // github.com/antonarhipov/ktor-workshop % git branch * main branch01 branch02 branch03 This is our starting point
data class. @Serializable data class User( val userId: Int, val userType: UserType, val displayName: String, val link: String, val aboutMe: String? = null ) enum class UserType { REGISTERED, MODERATOR, }
data class. @Serializable data class User( val userId: Int, val userType: UserType, val displayName: String, val link: String, val aboutMe: String? = null ) enum class UserType { REGISTERED, MODERATOR, } Tests
{ application { routing { get("/") { call.respondText("Hello World!") } } } client.get("/").apply { assertEquals(HttpStatusCode.OK, status) assertEquals("Hello World!", bodyAsText()) } } } It creates an application instance for each test. What if we want to reuse the application instance for several tests?
... } } val client = testApp.createClient {} @Test fun testRoot() = runBlocking<Unit> { val response = client.get("/") assertEquals(HttpStatusCode.OK, response.status) assertEquals("Hello World!", response.bodyAsText()) } } Con fi gure HttpClient for the TestApplication
... } } val client = testApp.createClient {} @Test fun testRoot() = runBlocking<Unit> { val response = client.get("/") assertEquals(HttpStatusCode.OK, response.status) assertEquals("Hello World!", response.bodyAsText()) } } Need to use runBlocking to run in a coroutine scope
... } } val client = testApp.createClient { install(io.ktor.client.plugins.contentnegotiation.ContentNegotiation) { json() } } @Test fun testRoot() = runBlocking<Unit> { . .. ContentNegotiation needs to be con fi gured for the HttpClient
... } } val client = testApp.createClient { install(io.ktor.client.plugins.contentnegotiation.ContentNegotiation) { json() } } @Test fun testRoot() = runBlocking<Unit> { . .. ContentNegotiation needs to be con fi gured for the HttpClient
User( val userId: Int, val userType: UserType, val displayName: String, val link: String, val aboutMe: String? = null ) enum class UserType { REGISTERED, MODERATOR, }
all data`(): Unit = runBlocking { ... } @Test fun `post data instance`(): Unit = runBlocking { ... } @Test fun `put data instance`(): Unit = runBlocking { ... } @Test fun `delete data instance`(): Unit = runBlocking { ... } @Serializable data class User( val userId: Int, val userType: UserType, val displayName: String, val link: String, val aboutMe: String? = null ) enum class UserType { REGISTERED, MODERATOR, } Implement these tests git branch * branch01
slf4jLogger() modules(usersDataModule) } val usersDataModule = module { single<IRepository> { RepositoryImpl() } } fun Application.configureRouting() { val repository by inject<IRepository>()
Inject the repository implementation (UserRepositoryImpl) using Koin Don't forget to adjust the tests code! Add Koin (or KodeinDI) dependencies to the project: implementation("io.insert-koin:koin-ktor:$koin_version") implementation("io.insert-koin:koin-logger-slf4j:$koin_version")
Datum : Table("data") { val id = integer("id").autoIncrement() val text = varchar("text", 255) } Datum.selectAll().map { Data(it[Datum.id], it[Datum.text]) } Select and map
Datum : Table("data") { val id = integer("id").autoIncrement() val text = varchar("text", 255) } Datum.selectAll().map { Data(it[Datum.id], it[Datum.text]) } Datum.selectAll().where { Datum.id eq id }.singleOrNull() ? . let { Data(it[Datum.id], it[Datum.text]) } Select and map
data classes 1. Add Exposed dependencies to the project val exposed_version = "0.50.1" implementation("org.jetbrains.exposed:exposed-core:$exposed_version") implementation("org.jetbrains.exposed:exposed-jdbc:$exposed_version") implementation("org.jetbrains.exposed:exposed-dao:$exposed_version") implementation("org.jetbrains.exposed:exposed-kotlin-datetime:$exposed_version") https://github.com/JetBrains/exposed
data classes 3. Implement UserRepository using Exposed with the real database 1. Add Exposed dependencies to the project val exposed_version = "0.50.1" implementation("org.jetbrains.exposed:exposed-core:$exposed_version") implementation("org.jetbrains.exposed:exposed-jdbc:$exposed_version") implementation("org.jetbrains.exposed:exposed-dao:$exposed_version") implementation("org.jetbrains.exposed:exposed-kotlin-datetime:$exposed_version") https://github.com/JetBrains/exposed
data classes 3. Implement UserRepository using Exposed with the real database 1. Add Exposed dependencies to the project 4. Integrate the new implementation into Ktor application using DI val exposed_version = "0.50.1" implementation("org.jetbrains.exposed:exposed-core:$exposed_version") implementation("org.jetbrains.exposed:exposed-jdbc:$exposed_version") implementation("org.jetbrains.exposed:exposed-dao:$exposed_version") implementation("org.jetbrains.exposed:exposed-kotlin-datetime:$exposed_version") https://github.com/JetBrains/exposed
text = varchar("text", 255) override val primaryKey = PrimaryKey(id) } object Items : Table("items_table") { val id = integer("id").autoIncrement() val name = varchar("name", 255) val datum_ref = reference("datum_ref", Datum.id) override val primaryKey = PrimaryKey(id) }
text = varchar("text", 255) override val primaryKey = PrimaryKey(id) } object Items : Table("items_table") { val id = integer("id").autoIncrement() val name = varchar("name", 255) val datum_ref = reference("datum_ref", Datum.id) override val primaryKey = PrimaryKey(id) }
text = varchar("text", 255) override val primaryKey = PrimaryKey(id) } object Items : Table("items_table") { val id = integer("id").autoIncrement() val name = varchar("name", 255) val datum_ref = reference("datum_ref", Datum.id) override val primaryKey = PrimaryKey(id) }
text = varchar("text", 255) override val primaryKey = PrimaryKey(id) } object Items : Table("items_table") { val id = integer("id").autoIncrement() val name = varchar("name", 255) val datum_ref = reference("datum_ref", Datum.id) override val primaryKey = PrimaryKey(id) }
text = varchar("text", 255) override val primaryKey = PrimaryKey(id) } object Items : IdTable<Int>("items_table") { override val id = integer("id").autoIncrement().entityId() val name = varchar("name", 255) val datum_ref = reference("datum_ref", Datum.id) override val primaryKey = PrimaryKey(id) }
companion object : LongEntityClass<DataEntity>(DatumEntityTable) } class ItemsEntity(id: EntityID<Int>) : IntEntity(id) { var name by ItemsEntityTable.name var data_ref by DataEntity referencedOn ItemsEntityTable.datum_ref companion object : IntEntityClass<ItemsEntity>(ItemsEntityTable) } Defining entities
companion object : LongEntityClass<DataEntity>(DatumEntityTable) } class ItemsEntity(id: EntityID<Int>) : IntEntity(id) { var name by ItemsEntityTable.name var data_ref by DataEntity referencedOn ItemsEntityTable.datum_ref companion object : IntEntityClass<ItemsEntity>(ItemsEntityTable) } Defining entities
val items by ItemsEntity referrersOn ItemsEntityTable.datum_ref companion object : LongEntityClass<DataEntity>(DatumEntityTable) } class ItemsEntity(id: EntityID<Int>) : IntEntity(id) { var name by ItemsEntityTable.name var data_ref by DataEntity referencedOn ItemsEntityTable.datum_ref companion object : IntEntityClass<ItemsEntity>(ItemsEntityTable) } Defining entities