Skip to content

Commit

Permalink
KTOR-3418 Added volume, so mongodb data is in a subfolder; the servic…
Browse files Browse the repository at this point in the history
…e closes when the server goes down; added tests
  • Loading branch information
VGoncharova committed Apr 27, 2023
1 parent b53847f commit 9b3ef36
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 21 deletions.
4 changes: 3 additions & 1 deletion mongodb/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ val mongodb_version: String by project

plugins {
kotlin("jvm") version "1.8.10"
id("io.ktor.plugin") version "2.2.3"
id("io.ktor.plugin") version "2.3.0"
id("org.jetbrains.kotlin.plugin.serialization") version "1.8.10"
}

Expand All @@ -25,12 +25,14 @@ dependencies {
implementation("io.ktor:ktor-server-core-jvm")
implementation("io.ktor:ktor-server-content-negotiation-jvm")
implementation("io.ktor:ktor-serialization-kotlinx-json-jvm")
implementation("io.ktor:ktor-serialization-gson-jvm")
implementation("io.ktor:ktor-server-netty-jvm")
implementation("ch.qos.logback:logback-classic:$logback_version")
implementation("io.ktor:ktor-server-config-yaml")
implementation("org.litote.kmongo:kmongo:$mongodb_version")
testImplementation("io.ktor:ktor-server-tests-jvm")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
testImplementation("io.ktor:ktor-server-test-host-jvm:2.3.0")
}

tasks.register("databaseInstance") {
Expand Down
4 changes: 4 additions & 0 deletions mongodb/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,9 @@ version: '3'
services:
mongodb:
image: mongodb/mongodb-community-server:6.0-ubi8
volumes:
- mongodata:/data/db
ports:
- 27017:27017
volumes:
mongodata:
13 changes: 12 additions & 1 deletion mongodb/src/main/kotlin/com/example/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,23 @@ package com.example

import io.ktor.server.application.*
import com.example.plugins.*
import com.example.service.ArticleService

fun main(args: Array<String>): Unit =
io.ktor.server.netty.EngineMain.main(args)

@Suppress("unused") // application.conf references the main function. This annotation prevents the IDE from marking it as unused.
fun Application.module() {
configureSerialization()
configureRouting()
val articleService = ArticleService()
configureRouting(articleService = articleService)
environment.monitor.subscribe(ApplicationStarted) { application ->
application.environment.log.info("Server is started")
}
environment.monitor.subscribe(ApplicationStopped) { application ->
application.environment.log.info("Server is stopped")
articleService.release()
application.environment.monitor.unsubscribe(ApplicationStarted) {}
application.environment.monitor.unsubscribe(ApplicationStopped) {}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.example.entities

fun Article.toDto(): ArticleDto =
ArticleDto(
fun Article.toDto(): CreateArticle =
CreateArticle(
id = this.id.toString(),
title = this.title,
body = this.body
)

fun ArticleDto.toArticle(): Article =
fun CreateArticle.toArticle(): Article =
Article(
title = this.title,
body = this.body
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.example.entities
import kotlinx.serialization.Serializable

@Serializable
data class ArticleDto(
data class CreateArticle(
val id: String? = null,
val title: String,
val body: String)
24 changes: 16 additions & 8 deletions mongodb/src/main/kotlin/com/example/plugins/Routing.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,37 @@ import io.ktor.server.response.*
import io.ktor.server.application.*
import io.ktor.server.request.*

fun Application.configureRouting() {
val articleService = ArticleService()
fun Application.configureRouting(articleService:ArticleService) {

routing {

post("/article") {
val request = call.receive<ArticleDto>()
val request = call.receive<CreateArticle>()
val article = request.toArticle()

articleService.create(article)?.let { userId ->
call.response.headers.append("My-User-Id-Header", userId.toString())
call.respond(HttpStatusCode.Created)
call.respond(HttpStatusCode.Created, userId.toString())
} ?: call.respond(HttpStatusCode.BadRequest, ErrorResponse.BAD_REQUEST_RESPONSE)
}

get("/articles") {
get("/article/list") {
val articlesList = articleService.findAll().map(Article::toDto)
call.respond(articlesList)
}

put("/article/edit/{id}") {

get("/article/{id}") {
val id = call.parameters["id"].toString()
val articleById = articleService.findById(id)?.toDto()
articleById?.let {
call.respond(articleById)
} ?: call.respond(HttpStatusCode.BadRequest)
}

put("/article/{id}/edit") {
val id = call.parameters["id"].toString()
val article = call.receive<ArticleDto>().toArticle()
val article = call.receive<CreateArticle>().toArticle()
val updatedSuccessfully = articleService.updateArticleById(id, article)
if (updatedSuccessfully) {
call.respond(HttpStatusCode.OK, "Article was edited")
Expand All @@ -39,7 +47,7 @@ fun Application.configureRouting() {
}
}

delete("/article/delete/{id}") {
delete("/article/{id}/delete") {
val id = call.parameters["id"].toString()

val deletedSuccessfully = articleService.deleteArticleById(id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ class ArticleService {
} ?: false

fun deleteArticleById(id: String): Boolean {
val deleteResult = articleCollection.deleteOne()
val bsonId: Id<Article> = ObjectId(id).toId()
val deleteResult = articleCollection.deleteOneById(bsonId)
return deleteResult.deletedCount == 1L
}

fun release() {
client.close()
}
}
94 changes: 88 additions & 6 deletions mongodb/src/test/kotlin/com/example/ApplicationTest.kt
Original file line number Diff line number Diff line change
@@ -1,21 +1,103 @@
package com.example

import com.example.entities.Article
import com.google.gson.Gson
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.server.testing.*
import kotlin.test.*
import io.ktor.http.*
import com.example.plugins.*
import com.example.service.ArticleService
import io.ktor.client.statement.*
import io.ktor.http.*

class ApplicationTest {
@Test
fun testRoot() = testApplication {
fun testPostArticle() = testApplication {
application {
val articleService = ArticleService()
configureRouting(articleService = articleService)
}
client.post("/article") {
header(HttpHeaders.Accept, ContentType.Application.Json)
contentType(ContentType.Application.Json)
val gson = Gson()
setBody(gson.toJson(Article(title="Who are you?", body="Whatever you are, be a good one")))
}.apply {
assertEquals(HttpStatusCode.Created, status)
}
}

@Test
fun testGetArticleId() = testApplication {
application {
val articleService = ArticleService()
configureRouting(articleService = articleService)
}
val testId = getTestArticleId()
client.get("/article/{id}") {
parameter("id", testId)
}.apply {
assertEquals(HttpStatusCode.OK, status)
}
}

@Test
fun testDeleteArticleIdDelete() = testApplication {
application {
configureRouting()
val articleService = ArticleService()
configureRouting(articleService = articleService)
}
client.get("/").apply {
val testId = getTestArticleId()
client.delete("/article/{id}/delete") {
parameter(key = "id", value = testId)
}.apply {
assertEquals(HttpStatusCode.OK, status)
assertEquals("Hello World!", bodyAsText())
assertEquals("Article was deleted", bodyAsText())
}
}

@Test
fun testPutArticleIdEdit() = testApplication {
application {
val articleService = ArticleService()
configureRouting(articleService = articleService)
}
client.put("/article/{id}/edit") {
parameter(key = "id", value = getTestArticleId())
header(HttpHeaders.Accept, ContentType.Application.Json)
contentType(ContentType.Application.Json)
val gson = Gson()
setBody(
gson.toJson(
Article(
title="Find new opportunities",
body="Opportunities don't happen, you create them"
)
)
)
}.apply {
assertEquals(HttpStatusCode.OK, status)
assertEquals("Article was edited", bodyAsText())
}
}

@Test
fun testGetArticleList() = testApplication {
application {
val articleService = ArticleService()
configureRouting(articleService = articleService)
}
client.get("/article/list").apply {
assertEquals(HttpStatusCode.OK, status)
}
}

fun getTestArticleId(): String? {
val article = Article(title = "title", body = "body")
val articleService = ArticleService()
articleService.create(article)?.let { userId ->
return userId.toString()
}
return null
}
}
4 changes: 4 additions & 0 deletions postgres/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ version: '3'
services:
db:
image: postgres:13.2-alpine
volumes:
- postgres:/var/lib/postgresql/data
ports:
- 5432:5432
extends:
file: postgres.yaml
service: postgres
volumes:
postgres:

0 comments on commit 9b3ef36

Please sign in to comment.