Skip to content

Commit

Permalink
Implement unmapped arguments schema inspection support
Browse files Browse the repository at this point in the history
  • Loading branch information
paulbakker committed May 29, 2024
1 parent 8adda29 commit 9021095
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.netflix.graphql.dgs.springgraphql

import com.netflix.graphql.dgs.InputArgument
import com.netflix.graphql.dgs.internal.DataFetcherReference
import com.netflix.graphql.dgs.internal.DgsSchemaProvider
import com.netflix.graphql.dgs.internal.SchemaProviderResult
Expand Down Expand Up @@ -89,7 +90,7 @@ class DgsGraphQLSourceBuilder(private val dgsSchemaProvider: DgsSchemaProvider)
throw IllegalStateException("Overriding the schema factory is not supported in this builder")
}

class SpringGraphQlDataFetcher(private val dataFetcher: DataFetcherReference) : SelfDescribingDataFetcher<Any> {
class DgsSelfDescribingDataFetcher(val dataFetcher: DataFetcherReference) : SelfDescribingDataFetcher<Any> {
override fun get(environment: DataFetchingEnvironment?): Any {
TODO("Not yet implemented")
}
Expand All @@ -99,12 +100,21 @@ class DgsGraphQLSourceBuilder(private val dgsSchemaProvider: DgsSchemaProvider)
override fun getReturnType(): ResolvableType {
return ResolvableType.forMethodReturnType(dataFetcher.method)
}

override fun getArguments(): Map<String, ResolvableType> {
return dataFetcher.method.parameters
.filter { it.isAnnotationPresent(InputArgument::class.java) }
.associate {
val name = it.getAnnotation(InputArgument::class.java).name.ifEmpty { it.name }
return@associate name to ResolvableType.forClass(it.type)
}
}
}

private fun wrapDataFetchers(dataFetchers: List<DataFetcherReference>): Map<String, Map<String, SelfDescribingDataFetcher<Any>>> {
val wrappedDataFetchers: MutableMap<String, MutableMap<String, SelfDescribingDataFetcher<Any>>> = mutableMapOf()
dataFetchers.forEach {
val wrappedDataFetcher = SpringGraphQlDataFetcher(it)
val wrappedDataFetcher = DgsSelfDescribingDataFetcher(it)
if (!wrappedDataFetchers.containsKey(it.parentType)) {
wrappedDataFetchers[it.parentType] = mutableMapOf()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import org.springframework.boot.autoconfigure.graphql.GraphQlProperties
import org.springframework.boot.autoconfigure.graphql.GraphQlSourceBuilderCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.graphql.execution.*
import java.util.Optional
import java.util.function.Consumer

@AutoConfiguration
@AutoConfigureBefore(name = ["org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration"])
Expand All @@ -52,7 +54,8 @@ open class DgsSpringGraphQLSourceAutoConfiguration {
wiringConfigurers: ObjectProvider<RuntimeWiringConfigurer>,
sourceCustomizers: ObjectProvider<GraphQlSourceBuilderCustomizer>,
reloadSchemaIndicator: DefaultDgsQueryExecutor.ReloadSchemaIndicator,
defaultExceptionHandler: DataFetcherExceptionHandler
defaultExceptionHandler: DataFetcherExceptionHandler,
reportConsumer: Optional<Consumer<SchemaReport>>
): GraphQlSource {
val dataFetcherExceptionResolvers: MutableList<DataFetcherExceptionResolver> = exceptionResolvers.orderedStream().toList().toMutableList()
dataFetcherExceptionResolvers.add((ExceptionHandlerResolverAdapter(defaultExceptionHandler)))
Expand All @@ -63,7 +66,30 @@ open class DgsSpringGraphQLSourceAutoConfiguration {
.instrumentation(instrumentations.orderedStream().toList())

if (properties.schema.inspection.isEnabled) {
builder.inspectSchemaMappings { message: SchemaReport? -> logger.info(message) }
if(reportConsumer.isPresent) {
builder.inspectSchemaMappings(reportConsumer.get())
} else {
builder.inspectSchemaMappings { message: SchemaReport? ->
val messageBuilder = StringBuilder("***Schema Report***\n")

val arguments = message?.unmappedArguments()?.map {
if(it.key is SelfDescribingDataFetcher) {
val dataFetcher =
(it.key as DgsGraphQLSourceBuilder.DgsSelfDescribingDataFetcher).dataFetcher
return@map dataFetcher.method.declaringClass.name + "." + dataFetcher.method.name + " for arguments " + it.value
} else {
return@map it.toString()
}
}

messageBuilder.append("Unmapped fields: ${message?.unmappedFields()}\n")
messageBuilder.append("Unmapped registrations: ${message?.unmappedRegistrations()}\n")
messageBuilder.append("Unmapped arguments: ${arguments}\n")
messageBuilder.append("Skipped types: ${message?.skippedTypes()}\n")

logger.info(messageBuilder.toString())
}
}
}

wiringConfigurers.orderedStream().forEach { configurer: RuntimeWiringConfigurer ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ package com.netflix.graphql.dgs.springgraphql.autoconfig
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.netflix.graphql.dgs.DgsComponent
import com.netflix.graphql.dgs.DgsQuery
import com.netflix.graphql.dgs.InputArgument
import com.netflix.graphql.dgs.autoconfig.DgsAutoConfiguration
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration
Expand All @@ -29,10 +31,13 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.graphql.data.method.annotation.QueryMapping
import org.springframework.graphql.execution.SchemaReport
import org.springframework.http.MediaType
import org.springframework.stereotype.Component
import org.springframework.stereotype.Controller
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.post
import java.util.function.Consumer

@SpringBootTest(
classes = [
Expand All @@ -45,14 +50,21 @@ import org.springframework.test.web.servlet.post
WebMvcAutoConfiguration::class
],

properties = ["dgs.graphql.schema-locations=classpath:/dgs-spring-graphql-smoke-test.graphqls"]
properties = [
"dgs.graphql.schema-locations=classpath:/dgs-spring-graphql-smoke-test.graphqls",
"spring.graphql.schema.inspection.enabled=true",
"dgs.graphql.schema-wiring-validation-enabled=false"
]
)
@AutoConfigureMockMvc
class DgsSpringGraphQlSmokeTest {

@Autowired
lateinit var mockMvc: MockMvc

@Autowired
lateinit var testReportConsumer: TestApp.TestReportConsumer

@Test
fun testGraphQlRequest() {
val query = """
Expand Down Expand Up @@ -84,6 +96,12 @@ class DgsSpringGraphQlSmokeTest {
}
}

@Test
fun testSchemaArgumentReporter() {
assertThat(testReportConsumer.schemaReport?.unmappedArguments()).hasSize(2)
assertThat(testReportConsumer.schemaReport?.unmappedArguments()?.values).containsExactly(listOf("someArg", "someOtherArg"), listOf("someArg"))
}

@TestConfiguration
open class TestApp {
@DgsComponent
Expand All @@ -92,6 +110,21 @@ class DgsSpringGraphQlSmokeTest {
fun dgsField(): String {
return "test from DGS"
}

@DgsQuery
fun unmappedArgument(@InputArgument someArg: String, @InputArgument someOtherArg: Int): String {
return "unmapped argument test"
}

@DgsQuery
fun incorrectNamedArgument(@InputArgument(name = "someArg") somename: String): String {
return "unmapped argument test"
}

@DgsQuery
fun mappedArguments(@InputArgument firstParam: String, @InputArgument secondParam: Int): String {
return "mapped argument test"
}
}

@Controller
Expand All @@ -101,5 +134,14 @@ class DgsSpringGraphQlSmokeTest {
return "test from Spring Controller"
}
}

@Component
open class TestReportConsumer: Consumer<SchemaReport> {
var schemaReport: SchemaReport? = null

override fun accept(schemaReport: SchemaReport) {
this.schemaReport = schemaReport
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
type Query {
dgsField: String
springControllerField: String
unmappedField: String
unmappedArgument: String
incorrectNamedArgument(somename: String): String
mappedArguments(firstParam: String, secondParam: Int): String
}

0 comments on commit 9021095

Please sign in to comment.