Skip to content

Commit

Permalink
Get Tags #157 (#176)
Browse files Browse the repository at this point in the history
  • Loading branch information
arioston authored Oct 5, 2023
1 parent 03ffb1e commit e17eb04
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 3 deletions.
9 changes: 7 additions & 2 deletions src/main/kotlin/io/github/nomisrev/env/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package io.github.nomisrev.env
import arrow.fx.coroutines.continuations.ResourceScope
import com.sksamuel.cohort.HealthCheckRegistry
import com.sksamuel.cohort.hikari.HikariConnectionsHealthCheck
import io.github.nomisrev.repo.TagPersistence
import io.github.nomisrev.repo.articleRepo
import io.github.nomisrev.repo.tagPersistence
import io.github.nomisrev.repo.userPersistence
import io.github.nomisrev.service.ArticleService
import io.github.nomisrev.service.JwtService
Expand All @@ -19,14 +21,16 @@ class Dependencies(
val userService: UserService,
val jwtService: JwtService,
val articleService: ArticleService,
val healthCheck: HealthCheckRegistry
val healthCheck: HealthCheckRegistry,
val tagPersistence: TagPersistence
)

suspend fun ResourceScope.dependencies(env: Env): Dependencies {
val hikari = hikari(env.dataSource)
val sqlDelight = sqlDelight(hikari)
val userRepo = userPersistence(sqlDelight.usersQueries)
val articleRepo = articleRepo(sqlDelight.articlesQueries, sqlDelight.tagsQueries)
val tagPersistence = tagPersistence(sqlDelight.tagsQueries)
val jwtService = jwtService(env.auth, userRepo)
val slugGenerator = slugifyGenerator()
val userService = userService(userRepo, jwtService)
Expand All @@ -40,6 +44,7 @@ suspend fun ResourceScope.dependencies(env: Env): Dependencies {
userService,
jwtService,
articleService(slugGenerator, articleRepo, userRepo),
checks
checks,
tagPersistence
)
}
2 changes: 2 additions & 0 deletions src/main/kotlin/io/github/nomisrev/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import io.github.nomisrev.env.Env
import io.github.nomisrev.env.configure
import io.github.nomisrev.env.dependencies
import io.github.nomisrev.routes.health
import io.github.nomisrev.routes.tagRoutes
import io.github.nomisrev.routes.userRoutes
import io.ktor.server.application.Application
import io.ktor.server.netty.Netty
Expand All @@ -26,4 +27,5 @@ fun Application.app(module: Dependencies) {
configure()
userRoutes(module.userService, module.jwtService)
health(module.healthCheck)
tagRoutes(module.tagPersistence)
}
13 changes: 13 additions & 0 deletions src/main/kotlin/io/github/nomisrev/repo/TagPersistence.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.github.nomisrev.repo

import io.github.nomisrev.sqldelight.TagsQueries

interface TagPersistence {
/** List all tags * */
suspend fun selectTags(): List<String>
}

fun tagPersistence(tags: TagsQueries) =
object : TagPersistence {
override suspend fun selectTags() = tags.selectTags().executeAsList()
}
24 changes: 24 additions & 0 deletions src/main/kotlin/io/github/nomisrev/routes/tags.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@file:Suppress("MatchingDeclarationName")

package io.github.nomisrev.routes

import io.github.nomisrev.repo.TagPersistence
import io.ktor.server.application.Application
import io.ktor.server.application.call
import io.ktor.server.response.respond
import io.ktor.server.routing.get
import io.ktor.server.routing.route
import io.ktor.server.routing.routing
import kotlinx.serialization.Serializable

@Serializable data class TagsResponse(val tags: List<String>)

fun Application.tagRoutes(tagPersistence: TagPersistence) = routing {
route("/tags") {
/* Registration: GET /api/tags */
get {
val tags = tagPersistence.selectTags()
call.respond(TagsResponse(tags))
}
}
}
4 changes: 3 additions & 1 deletion src/main/kotlin/io/github/nomisrev/service/ArticleService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ fun articleService(
override suspend fun createArticle(input: CreateArticle): Either<DomainError, Article> =
either {
val slug =
slugGenerator.generateSlug(input.title) { slug -> articlePersistence.exists(slug) }.bind()
slugGenerator
.generateSlug(input.title) { slug -> articlePersistence.exists(slug).not() }
.bind()
val createdAt = OffsetDateTime.now()
val articleId =
articlePersistence
Expand Down
63 changes: 63 additions & 0 deletions src/test/kotlin/io/github/nomisrev/routes/TagRouteSpec.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package io.github.nomisrev.routes

import arrow.core.flatMap
import io.github.nefilim.kjwt.JWSHMAC512Algorithm
import io.github.nefilim.kjwt.JWT
import io.github.nomisrev.repo.UserId
import io.github.nomisrev.service.CreateArticle
import io.github.nomisrev.service.RegisterUser
import io.github.nomisrev.withServer
import io.kotest.assertions.arrow.core.shouldBeRight
import io.kotest.assertions.arrow.core.shouldBeSome
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.shouldBe
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.http.contentType

class TagRouteSpec :
StringSpec({
// User
val validUsername = "username2"
val validEmail = "[email protected]"
val validPw = "123456789"
// Article
val validTags = setOf("arrow", "ktor", "kotlin", "sqldelight")
val validTitle = "Fake Article Arrow "
val validDescription = "This is a fake article description."
val validBody = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."

"Check for empty list retrieval" {
withServer {
val response = get("/tags") { contentType(ContentType.Application.Json) }

response.status shouldBe HttpStatusCode.OK
response.body<TagsResponse>().tags.toSet() shouldBe validTags
}
}

"Can get all tags" {
withServer { dependencies ->
val userId =
dependencies.userService
.register(RegisterUser(validUsername, validEmail, validPw))
.flatMap { JWT.decodeT(it.value, JWSHMAC512Algorithm) }
.map { it.claimValueAsLong("id").shouldBeSome() }
.shouldBeRight()

dependencies.articleService
.createArticle(
CreateArticle(UserId(userId), validTitle, validDescription, validBody, validTags)
)
.shouldBeRight()

val response = get("/tags") { contentType(ContentType.Application.Json) }

response.status shouldBe HttpStatusCode.OK
response.body<TagsResponse>().tags shouldHaveSize 4
}
}
})

0 comments on commit e17eb04

Please sign in to comment.