Skip to content

Commit

Permalink
chore: clean codes
Browse files Browse the repository at this point in the history
  • Loading branch information
hantsy committed Jan 3, 2025
1 parent ef77345 commit b58d5fb
Show file tree
Hide file tree
Showing 17 changed files with 167 additions and 110 deletions.
27 changes: 14 additions & 13 deletions dgs-kotlin-co/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import com.netflix.graphql.dgs.codegen.gradle.GenerateJavaTask
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.config.ApiVersion.Companion.KOTLIN_2_0
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion

Expand All @@ -18,9 +16,9 @@ group = "com.example"
version = "0.0.1-SNAPSHOT"

java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}

repositories {
Expand All @@ -32,13 +30,14 @@ repositories {
dependencyManagement {
imports {
mavenBom("com.netflix.graphql.dgs:graphql-dgs-platform-dependencies:10.0.1")
mavenBom("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.1")
mavenBom("io.kotest:kotest-bom:5.9.1")
}
}

dependencies {
//implementation(platform("com.netflix.graphql.dgs:graphql-dgs-platform-dependencies:8.1.1"))
implementation("com.netflix.graphql.dgs:dgs-starter")
implementation("io.projectreactor:reactor-core:3.7.1")

//Spring
implementation("org.springframework.boot:spring-boot-starter-webflux")
Expand All @@ -53,22 +52,23 @@ dependencies {
implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")

//kotlin coroutines extensions
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.10.1")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.9.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")

// test
testImplementation("org.springframework.boot:spring-boot-starter-test") {
exclude(module = "mockito-core")
}
testImplementation("com.netflix.graphql.dgs:dgs-starter-test")
testImplementation("io.projectreactor:reactor-test")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.1")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test")
testImplementation("io.mockk:mockk-jvm:1.13.14")
testImplementation("com.ninja-squad:springmockk:4.0.2"){
testImplementation("com.ninja-squad:springmockk:4.0.2") {
exclude(module = "mockk")
}
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
testImplementation("io.kotest:kotest-assertions-core-jvm:5.9.1")
testImplementation("io.kotest:kotest-framework-concurrency:5.9.1")
testImplementation("io.kotest:kotest-runner-junit5-jvm")
testImplementation("io.kotest:kotest-assertions-core-jvm")
testImplementation("io.kotest:kotest-framework-concurrency")
}

tasks.withType<GenerateJavaTask> {
Expand All @@ -79,6 +79,7 @@ tasks.withType<GenerateJavaTask> {
shortProjectionNames = false
maxProjectionDepth = 2
snakeCaseConstantNames = true
typeMapping = mutableMapOf("UUID" to "java.util.UUID")
}


Expand Down
10 changes: 5 additions & 5 deletions dgs-kotlin-co/src/main/kotlin/com/example/demo/Extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ import com.example.demo.model.CommentEntity
import com.example.demo.model.PostEntity

fun PostEntity.asGqlType(): Post = Post(
id = this.id!!.toString(),
id = this.id!!,
title = this.title,
content = this.content,
createdAt = this.createdAt,
authorId = this.authorId.toString()
authorId = this.authorId
)

fun CommentEntity.asGqlType(): Comment = Comment(
id = this.id!!.toString(),
id = this.id!!,
content = this.content,
createdAt = this.createdAt,
postId = this.postId!!.toString()
postId = this.postId!!
)

fun AuthorEntity.asGqlType(): Author = Author(
id = this.id!!.toString(),
id = this.id!!,
name = this.name,
email = this.email,
createdAt = this.createdAt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.example.demo.service.AuthorService
import com.example.demo.service.PostService
import com.netflix.graphql.dgs.*
import kotlinx.coroutines.flow.toList
import java.util.UUID

@DgsComponent
class AuthorsDataFetcher(
Expand All @@ -15,7 +16,7 @@ class AuthorsDataFetcher(
) {

@DgsQuery
suspend fun author(@InputArgument authorId: String) = authorService.getAuthorById(authorId)
suspend fun author(@InputArgument authorId: UUID) = authorService.getAuthorById(authorId)

@DgsData(parentType = DgsConstants.AUTHOR.TYPE_NAME, field = DgsConstants.AUTHOR.Posts)
suspend fun posts(dfe: DgsDataFetchingEnvironment): List<Post> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.example.demo.gql.types.*
import com.example.demo.service.PostService
import com.netflix.graphql.dgs.*
import kotlinx.coroutines.flow.toList
import java.util.UUID
import java.util.concurrent.CompletableFuture

@DgsComponent
Expand All @@ -17,18 +18,18 @@ class PostsDataFetcher(val postService: PostService) {
suspend fun allPosts(): List<Post> = postService.allPosts().toList()

@DgsQuery
suspend fun postById(@InputArgument postId: String) = postService.getPostById(postId)
suspend fun postById(@InputArgument postId: UUID) = postService.getPostById(postId)

@DgsData(parentType = DgsConstants.POST.TYPE_NAME, field = DgsConstants.POST.Author)
fun author(dfe: DgsDataFetchingEnvironment): CompletableFuture<Author> {
val dataLoader = dfe.getDataLoader<String, Author>("authorsLoader")
val dataLoader = dfe.getDataLoader<UUID, Author>("authorsLoader")
val post = dfe.getSource<Post>()
return dataLoader!!.load(post!!.authorId)
}

@DgsData(parentType = DgsConstants.POST.TYPE_NAME, field = DgsConstants.POST.Comments)
fun comments(dfe: DgsDataFetchingEnvironment): CompletableFuture<List<Comment>> {
val dataLoader = dfe.getDataLoader<String, List<Comment>>(CommentsDataLoader::class.java)
val dataLoader = dfe.getDataLoader<UUID, List<Comment>>(CommentsDataLoader::class.java)
val (id) = dfe.getSource<Post>()!!
return dataLoader.load(id)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.future.future
import org.dataloader.BatchLoader
import java.util.UUID
import java.util.concurrent.CompletionStage
import java.util.concurrent.Executors

@DgsDataLoader(name = "authorsLoader")
class AuthorsDataLoader(val authorService: AuthorService) : BatchLoader<String, Author> {
class AuthorsDataLoader(val authorService: AuthorService) : BatchLoader<UUID, Author> {
val loaderScope = CoroutineScope(Executors.newCachedThreadPool().asCoroutineDispatcher())
override fun load(keys: List<String>): CompletionStage<List<Author>> = loaderScope.future {
override fun load(keys: List<UUID>): CompletionStage<List<Author>> = loaderScope.future {
authorService.getAuthorByIdIn(keys).toList()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,20 @@ import kotlinx.coroutines.future.future
import org.dataloader.MappedBatchLoader
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.util.UUID
import java.util.concurrent.CompletionStage
import java.util.concurrent.Executors

@DgsDataLoader(name = "commentsLoader")
class CommentsDataLoader(val postService: PostService) : MappedBatchLoader<String, List<Comment>> {
class CommentsDataLoader(val postService: PostService) : MappedBatchLoader<UUID, List<Comment>> {
val loaderScope = CoroutineScope(Executors.newCachedThreadPool().asCoroutineDispatcher())
companion object {
val log: Logger = LoggerFactory.getLogger(CommentsDataLoader::class.java)
}

override fun load(keys: Set<String>): CompletionStage<Map<String, List<Comment>>> = loaderScope.future {
val comments = postService.getCommentsByPostIdIn(keys).toList()
val mappedComments: MutableMap<String, List<Comment>> = mutableMapOf()
override fun load(keys: Set<UUID>): CompletionStage<Map<UUID, List<Comment>>> = loaderScope.future {
val comments = postService.getCommentsByPostIdIn(keys.toList()).toList()
val mappedComments: MutableMap<UUID, List<Comment>> = mutableMapOf()
keys.forEach { mappedComments[it] = comments.filter { c -> c.postId == it } }
log.info("mapped comments: {}", mappedComments)
mappedComments
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.example.demo.gql.scalars

import com.netflix.graphql.dgs.DgsScalar
import graphql.GraphQLContext
import graphql.execution.CoercedVariables
import graphql.language.StringValue
import graphql.language.Value
import graphql.schema.Coercing
import graphql.schema.CoercingParseLiteralException
import graphql.schema.CoercingSerializeException
import java.util.*

@DgsScalar(name = "UUID")
class UUIDScalar : Coercing<UUID, String> {

override fun valueToLiteral(
input: Any,
graphQLContext: GraphQLContext,
locale: Locale
): Value<*> = StringValue.of(input.toString())

override fun parseLiteral(
input: Value<*>,
variables: CoercedVariables,
graphQLContext: GraphQLContext,
locale: Locale
): UUID? {
if (input is StringValue) {
return UUID.fromString(input.value);
}

throw CoercingParseLiteralException("Value is not a valid UUID string");
}

override fun parseValue(
input: Any,
graphQLContext: GraphQLContext,
locale: Locale
): UUID? = UUID.fromString(input.toString())

override fun serialize(
dataFetcherResult: Any,
graphQLContext: GraphQLContext,
locale: Locale
): String? {
if (dataFetcherResult is UUID) {
return dataFetcherResult.toString();
}

throw CoercingSerializeException("Not a valid UUID");
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
package com.example.demo.service

class AuthorNotFoundException(id: String) : RuntimeException("Author: $id was not found.")
import java.util.*

class AuthorNotFoundException(id: UUID) : RuntimeException("Author: $id was not found.")

Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@ import java.util.*
@Service
class AuthorService(val authors: AuthorRepository) {

suspend fun getAuthorById(id: String): Author {
val author = this.authors.findById(UUID.fromString(id)) ?: throw AuthorNotFoundException(id)
suspend fun getAuthorById(id: UUID): Author {
val author = this.authors.findById(id) ?: throw AuthorNotFoundException(id)
return author.asGqlType()
}

// alternative to use kotlin co `Flow`
fun getAuthorByIdIn(ids: List<String>): Flow<Author> {
val uuids = ids.map { UUID.fromString(it) };
return authors.findAllById(uuids).map { it.asGqlType() }
fun getAuthorByIdIn(ids: List<UUID>): Flow<Author> {
return authors.findAllById(ids).map { it.asGqlType() }
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package com.example.demo.service

import com.example.demo.model.CommentEntity
import com.example.demo.model.PostEntity
import com.example.demo.asGqlType
import com.example.demo.gql.types.Comment
import com.example.demo.gql.types.CommentInput
import com.example.demo.gql.types.CreatePostInput
import com.example.demo.gql.types.Post
import com.example.demo.model.CommentEntity
import com.example.demo.model.PostEntity
import com.example.demo.repository.CommentRepository
import com.example.demo.repository.PostRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.map
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import reactor.core.publisher.Flux
import reactor.core.publisher.Sinks
import java.util.*

@Service
Expand All @@ -26,13 +26,13 @@ class DefaultPostService(

override fun allPosts() = this.posts.findAll().map { it.asGqlType() }

override suspend fun getPostById(id: String): Post {
val post = this.posts.findById(UUID.fromString(id)) ?: throw PostNotFoundException(id)
override suspend fun getPostById(id: UUID): Post {
val post = this.posts.findById(id) ?: throw PostNotFoundException(id)
return post.asGqlType()
}

override fun getPostsByAuthorId(id: String): Flow<Post> {
return this.posts.findByAuthorId(UUID.fromString(id))
override fun getPostsByAuthorId(id: UUID): Flow<Post> {
return this.posts.findByAuthorId(id)
.map { it.asGqlType() }
}

Expand All @@ -43,30 +43,29 @@ class DefaultPostService(
}

override suspend fun addComment(commentInput: CommentInput): Comment {
val postId = UUID.fromString(commentInput.postId)
val postId = commentInput.postId
if (!this.posts.existsById(postId)) {
throw PostNotFoundException(postId.toString())
throw PostNotFoundException(postId)
}
val data = CommentEntity(content = commentInput.content, postId = postId)
val savedComment = this.comments.save(data)
val comment = savedComment.asGqlType()
sink.emitNext(comment, Sinks.EmitFailureHandler.FAIL_FAST)
sink.emit(comment)

return comment
}

val sink = Sinks.many().replay().latest<Comment>()
val sink = MutableSharedFlow<Comment>(replay = 1)

// subscription: commentAdded
override fun commentAdded(): Flux<Comment> = sink.asFlux()
override fun commentAdded(): Flow<Comment> = sink.asSharedFlow()

override fun getCommentsByPostId(id: String): Flow<Comment> {
return this.comments.findByPostId(UUID.fromString(id))
override fun getCommentsByPostId(id: UUID): Flow<Comment> {
return this.comments.findByPostId(id)
.map { it.asGqlType() }
}

override fun getCommentsByPostIdIn(ids: Set<String>): Flow<Comment> {
val uuids = ids.map { UUID.fromString(it) };
return comments.findByPostIdIn(uuids).map { it.asGqlType() }
override fun getCommentsByPostIdIn(ids: List<UUID>): Flow<Comment> {
return comments.findByPostIdIn(ids).map { it.asGqlType() }
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
package com.example.demo.service

class PostNotFoundException(id: String) : RuntimeException("Post: $id was not found.")
import java.util.UUID

class PostNotFoundException(id: UUID) : RuntimeException("Post: $id was not found.")
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,20 @@ import com.example.demo.gql.types.CreatePostInput
import com.example.demo.gql.types.Post
import kotlinx.coroutines.flow.Flow
import reactor.core.publisher.Flux
import java.util.UUID

interface PostService {
fun allPosts(): Flow<Post>

suspend fun getPostById(id: String): Post
fun getPostsByAuthorId(id: String): Flow<Post>
suspend fun getPostById(id: UUID): Post
fun getPostsByAuthorId(id: UUID): Flow<Post>

suspend fun createPost(postInput: CreatePostInput): Post

suspend fun addComment(commentInput: CommentInput): Comment

// subscription: commentAdded
fun commentAdded(): Flux<Comment>
fun getCommentsByPostId(id: String): Flow<Comment>
fun getCommentsByPostIdIn(ids: Set<String>): Flow<Comment>
fun commentAdded(): Flow<Comment>
fun getCommentsByPostId(id: UUID): Flow<Comment>
fun getCommentsByPostIdIn(ids: List<UUID>): Flow<Comment>
}
Loading

0 comments on commit b58d5fb

Please sign in to comment.