Skip to content

Commit

Permalink
chore: fix errors
Browse files Browse the repository at this point in the history
  • Loading branch information
hantsy committed Nov 25, 2024
1 parent 43737da commit 9732c1e
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 111 deletions.
29 changes: 16 additions & 13 deletions dgs-kotlin-co/src/main/kotlin/com/example/demo/DemoApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@ import com.example.demo.model.PostEntity
import com.example.demo.repository.CommentRepository
import com.example.demo.repository.PostRepository
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
import org.slf4j.LoggerFactory
import org.springframework.boot.ApplicationArguments
import org.springframework.boot.ApplicationRunner
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.event.ApplicationReadyEvent
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.event.EventListener
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.web.server.SecurityWebFilterChain
Expand All @@ -26,20 +25,24 @@ fun main(args: Array<String>) {
}

@Component
class DataInitializer(val posts: PostRepository, val comments: CommentRepository) : ApplicationRunner {
private val log = LoggerFactory.getLogger(DataInitializer::class.java)
override fun run(args: ApplicationArguments?) {
class DataInitializer(val posts: PostRepository, val comments: CommentRepository) {

companion object {
private val log = LoggerFactory.getLogger(DataInitializer::class.java)
}

@EventListener(ApplicationReadyEvent::class)
suspend fun init() {
val data = listOf(
PostEntity(title = "Learn Spring", content = "content of Learn Spring"),
PostEntity(title = "Learn Dgs framework", content = "content of Learn Dgs framework")
)
runBlocking {
comments.deleteAll()
posts.deleteAll()

val saved = posts.saveAll(data).toList()
saved.forEach { log.debug("saved: {}", it) }
}
comments.deleteAll()
posts.deleteAll()

val saved = posts.saveAll(data).toList()
saved.forEach { log.debug("saved: {}", it) }
}
}

Expand All @@ -51,7 +54,7 @@ class SecurityConfig {
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http
.csrf { it.disable() }
.httpBasic{}
.httpBasic {}
.securityMatcher(PathPatternParserServerWebExchangeMatcher("/graphql"))
.build()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class ExceptionHandlers : DataFetcherExceptionHandler {
when (val exception = handlerParameters.exception) {
is PostNotFoundException, is AuthorNotFoundException -> {
val graphqlError = TypedGraphQLError.newNotFoundBuilder()
.message(exception.message)
.message(exception.message?: "Not Found")
.path(handlerParameters.path)
.build();
CompletableFuture.completedFuture(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class AuthorsDataFetcher(

@DgsData(parentType = DgsConstants.AUTHOR.TYPE_NAME, field = DgsConstants.AUTHOR.Posts)
suspend fun posts(dfe: DgsDataFetchingEnvironment): List<Post> {
val a: Author = dfe.getSource()
val a: Author = dfe.getSource()!!
return postService.getPostsByAuthorId(a.id).toList()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ class PostsDataFetcher(val postService: PostService) {
fun author(dfe: DgsDataFetchingEnvironment): CompletableFuture<Author> {
val dataLoader = dfe.getDataLoader<String, Author>("authorsLoader")
val post = dfe.getSource<Post>()
return dataLoader.load(post.authorId)
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 (id) = dfe.getSource<Post>()
val (id) = dfe.getSource<Post>()!!
return dataLoader.load(id)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,47 @@
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.CoercingParseValueException
import graphql.schema.CoercingSerializeException
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter


//@DgsComponent
//class DateTimeScalar {
// @DgsRuntimeWiring
// fun addScalar(builder: RuntimeWiring.Builder): RuntimeWiring.Builder {
// return builder.scalar(ExtendedScalars.DateTime)
// }
//}
import java.util.*

@DgsScalar(name = "LocalDateTime")
class LocalDateTimeScalar : Coercing<LocalDateTime, String> {
@Throws(CoercingSerializeException::class)
override fun serialize(dataFetcherResult: Any): String? {
override fun serialize(dataFetcherResult: Any, graphQLContext: GraphQLContext, locale: Locale): String? {
return when (dataFetcherResult) {
is LocalDateTime -> dataFetcherResult.format(DateTimeFormatter.ISO_DATE_TIME)
else -> throw CoercingSerializeException("Not a valid DateTime")
}
}

@Throws(CoercingParseValueException::class)
override fun parseValue(input: Any): LocalDateTime {
override fun parseValue(input: Any, graphQLContext: GraphQLContext, locale: Locale): LocalDateTime? {
return LocalDateTime.parse(input.toString(), DateTimeFormatter.ISO_DATE_TIME)
}

@Throws(CoercingParseLiteralException::class)
override fun parseLiteral(input: Any): LocalDateTime {
override fun parseLiteral(
input: Value<*>,
variables: CoercedVariables,
graphQLContext: GraphQLContext,
locale: Locale
): LocalDateTime? {
when (input) {
is StringValue -> return LocalDateTime.parse(input.value, DateTimeFormatter.ISO_DATE_TIME)
else -> throw CoercingParseLiteralException("Value is not a valid ISO date time")
}
}

override fun valueToLiteral(input: Any, graphQLContext: GraphQLContext, locale: Locale): Value<*> {
return when (input) {
is String -> StringValue.newStringValue(input).build()
else -> throw CoercingParseValueException("Value is not a string")
}
}
}
Original file line number Diff line number Diff line change
@@ -1,76 +1,94 @@
package com.example.demo

import com.jayway.jsonpath.TypeRef
import com.netflix.graphql.dgs.DgsQueryExecutor
import com.netflix.graphql.dgs.client.WebClientGraphQLClient
import com.netflix.graphql.dgs.client.WebSocketGraphQLClient
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.web.server.LocalServerPort
import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient
import reactor.test.StepVerifier
import java.time.Duration

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class DemoApplicationTestsWithGraphQLClient {

@Autowired
lateinit var dgsQueryExecutor: DgsQueryExecutor

lateinit var webClientGraphQLClient: WebClientGraphQLClient
lateinit var socketGraphQLClient: WebSocketGraphQLClient

@LocalServerPort
var port: Int = 0

@BeforeEach
fun setup() {
this.socketGraphQLClient = WebSocketGraphQLClient("ws://localhost:$port/subscriptions", ReactorNettyWebSocketClient())
this.webClientGraphQLClient = WebClientGraphQLClient(WebClient.create("http://localhost:$port/graphql"))
this.socketGraphQLClient =
WebSocketGraphQLClient("ws://localhost:$port/subscriptions", ReactorNettyWebSocketClient())
}

@Test
fun testMessages() {
//Hooks.onOperatorDebug();
val query = "subscription { messageSent { body } }"
val messageSentSubscriptionQuery = """
subscription {
messageSent {
body
}
}
""".trimIndent()
val variables = emptyMap<String, Any>()
val executionResult = socketGraphQLClient.reactiveExecuteQuery(query, variables)
val executionResult = socketGraphQLClient.reactiveExecuteQuery(messageSentSubscriptionQuery, variables)
.map {
it.extractValueAsObject(
"data.messageSent",
object : TypeRef<Map<String, Any>>() {}
)["body"] as String
}

val message1 = "text1"
val message2 = "text2"
val verifier = StepVerifier.create(executionResult)
.consumeNextWith { assertThat(it).isEqualTo("text1 message") }
// .consumeNextWith { assertThat(it).isEqualTo("text2 message") }
.thenAwait(Duration.ofMillis(1000)) // see: https://github.com/Netflix/dgs-framework/issues/657
.consumeNextWith { assertThat(it).isEqualTo(message1) }
.consumeNextWith { assertThat(it).isEqualTo(message2) }
.thenCancel()
.verifyLater()

val sendText1 = dgsQueryExecutor.executeAndExtractJsonPath<String>(
"mutation sendMessage(\$msg: TextMessageInput!) { send(message:\$msg) { body}}",
"data.send.body",
mapOf("msg" to (mapOf("body" to "text1 message")))
)
assertThat(sendText1).contains("text1");
val sendMessageQuery ="""
mutation sendMessage(${"$"}msg: TextMessageInput!) {
send(message:${"$"}msg) {
body
}
}
""".trimMargin()
webClientGraphQLClient.reactiveExecuteQuery(sendMessageQuery, mapOf("msg" to (mapOf("body" to message1))))
.map { it.extractValueAsObject("data.send.body", String::class.java) }
.`as` { StepVerifier.create(it) }
.consumeNextWith { assertThat(it).isEqualTo(message1) }
.verifyComplete()

// val sendText2 = dgsQueryExecutor.executeAndExtractJsonPath<String>(
// "mutation sendMessage(\$msg: TextMessageInput!) { send(message:\$msg) { body}}",
// "data.send.body",
// mapOf("msg" to (mapOf("body" to "text2 message")))
// )
// assertThat(sendText2).contains("text2");
webClientGraphQLClient.reactiveExecuteQuery(sendMessageQuery, mapOf("msg" to (mapOf("body" to message2))))
.map { it.extractValueAsObject("data.send.body", String::class.java) }
.`as` { StepVerifier.create(it) }
.consumeNextWith { assertThat(it).isEqualTo(message2) }
.verifyComplete()

//verify it now.
verifier.verify()

val msgs = dgsQueryExecutor.executeAndExtractJsonPath<List<String>>(
" { messages { body }}",
"data.messages[*].body"
)
assertThat(msgs).allMatch { s: String ->
s.contains(
"message"
)
}
val allMessagesQuery = """
{
messages{
body
}
}
""".trimIndent();
webClientGraphQLClient.reactiveExecuteQuery(allMessagesQuery)
.map { it.extractValueAsObject("data.messages[*].body", object : TypeRef<List<String>>() {}) }
.`as` { StepVerifier.create(it) }
.consumeNextWith { assertThat(it).isEqualTo(message1) }
.consumeNextWith { assertThat(it).isEqualTo(message2) }
.verifyComplete()
}
}
6 changes: 3 additions & 3 deletions dgs-webflux/src/test/java/com/example/demo/MutationTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

Expand Down Expand Up @@ -45,10 +45,10 @@ static class MutationTestsConfig {
@Autowired
DgsReactiveQueryExecutor dgsQueryExecutor;

@MockBean
@MockitoBean
PostService postService;

@MockBean
@MockitoBean
AuthorService authorService;

@Test
Expand Down
5 changes: 3 additions & 2 deletions dgs-webflux/src/test/java/com/example/demo/QueryTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
Expand All @@ -35,10 +36,10 @@ class QueryTests {
@Autowired
DgsReactiveQueryExecutor dgsQueryExecutor;

@MockBean
@MockitoBean
PostService postService;

@MockBean
@MockitoBean
AuthorService authorService;

@Configuration
Expand Down
26 changes: 17 additions & 9 deletions dgs-webflux/src/test/java/com/example/demo/SubscriptionTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
import reactor.core.publisher.Mono;

import java.util.Collections;
Expand Down Expand Up @@ -65,19 +65,19 @@ static class SubscriptionTestsConfig {
@Autowired
ObjectMapper objectMapper;

@SpyBean
@MockitoSpyBean
PostService postService;

@MockBean
@MockitoBean
PostRepository postRepository;

@MockBean
@MockitoBean
CommentRepository commentRepository;

@MockBean
@MockitoBean
AuthorRepository authorRepository;

@MockBean
@MockitoBean
AuthorService authorService;

@SneakyThrows
Expand All @@ -98,7 +98,15 @@ void createCommentAndSubscription() {
// commentAdded producer
var comments = new CopyOnWriteArrayList<Comment>();

@Language("GraphQL") var subscriptionQuery = "subscription onCommentAdded { commentAdded { id postId content } }";
@Language("GraphQL") var subscriptionQuery = """
subscription onCommentAdded {
commentAdded {
id
postId
content
}
}
""".stripIndent();
// var executionResult = dgsReactiveQueryExecutor.execute(subscriptionQuery, Collections.emptyMap()).block();
// var publisher = executionResult.<Publisher<ExecutionResult>>getData();
// publisher.subscribe(new Subscriber<ExecutionResult>() {
Expand Down Expand Up @@ -130,7 +138,7 @@ void createCommentAndSubscription() {
// });
//
var executionResultMono = dgsReactiveQueryExecutor.execute(subscriptionQuery, Collections.emptyMap());
var publisher = executionResultMono.flatMapMany(result -> result.<Publisher<ExecutionResult>>getData());
var publisher = executionResultMono.flatMapMany(ExecutionResult::<Publisher<ExecutionResult>>getData);
publisher.subscribe(executionResult -> {
log.debug("execution result in publisher: {}", executionResult);
var commentAdded = objectMapper.convertValue(
Expand Down
Loading

0 comments on commit 9732c1e

Please sign in to comment.