Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ExpediaGroup/graphql-kotlin
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 8.3.0
Choose a base ref
...
head repository: ExpediaGroup/graphql-kotlin
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 8.4.0
Choose a head ref
  • 3 commits
  • 14 files changed
  • 4 contributors

Commits on Feb 4, 2025

  1. fix: remove additional supplier invoke on APQ after caching entries (#…

    …2073)
    
    ### 📝 Description
    Fixes duplicate call of supplier on APQ caching.
    
    ### 🔗 Related Issues
    #2072
    malaquf authored Feb 4, 2025
    Copy the full SHA
    fa6b12d View commit details

Commits on Mar 12, 2025

  1. Add support for generated @deprecated arguments from @GraphQLDeprecat…

    …ed (#2075)
    
    ### 📝 Description  
    This PR enables support for using the `@GraphQLDeprecated` annotation on
    arguments, allowing the generated GraphQL schema to include the
    `@deprecated` directive.
    
    #### Changes:  
    - Updated `generateArgument.kt ` to mark argument as deprecated if the
    DeprecationReason is present.
    - Added tests to validate the correct behavior when marking arguments as
    deprecated.
    
    #### Why?  
    The GraphQL spec now officially supports applying `@deprecated` on
    arguments, enabling better API evolution without breaking existing
    clients. This change aligns the library with the latest spec, giving
    developers a built-in way to signal argument deprecations.
    
    - [GraphQL Spec - Deprecated
    Arguments](https://spec.graphql.org/October2021/#sec--deprecated)
    ](https://spec.graphql.org/draft/#sec--deprecated)
    
    ### 🔗 Related Issues  
    #1361
    sanchezdale authored Mar 12, 2025
    Copy the full SHA
    ce8124a View commit details

Commits on Mar 18, 2025

  1. feat: update fastjson2 (#2076)

    benchmarks 
    
    GraphQLRequest deserialization jackson vs fastjson2
    
    ![image](https://github.com/user-attachments/assets/aa31c409-ec2c-4d3e-b447-690da2c00980)
    
    
    GraphQLResponse serialization Jackson vs fastjson2
    
    ![image](https://github.com/user-attachments/assets/45934224-5e20-447f-955f-9736ae931aab)
    
    ---------
    
    Co-authored-by: Samuel Vazquez <samvazquez@expediagroup.com>
    samuelAndalon and Samuel Vazquez authored Mar 18, 2025
    Copy the full SHA
    6d24045 View commit details
Showing with 609 additions and 429 deletions.
  1. +1 −1 ...eries/src/main/kotlin/com/expediagroup/graphql/apq/cache/DefaultAutomaticPersistedQueriesCache.kt
  2. +5 −1 ...a-generator/src/main/kotlin/com/expediagroup/graphql/generator/internal/types/generateArgument.kt
  3. +15 −0 ...-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/directives/DirectiveTests.kt
  4. +14 −0 ...nerator/src/test/kotlin/com/expediagroup/graphql/generator/internal/types/GenerateArgumentTest.kt
  5. +1 −1 gradle/libs.versions.toml
  6. +15 −10 .../graphql-kotlin-server/src/benchmarks/kotlin/GraphQLServerRequestBatchDeserializationBenchmark.kt
  7. +3 −3 servers/graphql-kotlin-server/src/benchmarks/kotlin/GraphQLServerRequestDeserializationBenchmark.kt
  8. +23 −10 ...s/graphql-kotlin-server/src/benchmarks/kotlin/GraphQLServerResponseBatchSerializationBenchmark.kt
  9. +3 −6 servers/graphql-kotlin-server/src/benchmarks/kotlin/GraphQLServerResponseSerializationBenchmark.kt
  10. +373 −375 servers/graphql-kotlin-server/src/benchmarks/resources/StarWarsDetailsResponse.json
  11. +1 −1 ...phql-kotlin-server/src/main/kotlin/com/expediagroup/graphql/server/types/GraphQLServerResponse.kt
  12. +57 −14 ...l-kotlin-server/src/test/kotlin/com/expediagroup/graphql/server/types/GraphQLServerRequestTest.kt
  13. +85 −7 ...-kotlin-server/src/test/kotlin/com/expediagroup/graphql/server/types/GraphQLServerResponseTest.kt
  14. +13 −0 website/versioned_docs/version-8.x.x/schema-generator/customizing-schemas/deprecating-schema.md
Original file line number Diff line number Diff line change
@@ -35,6 +35,6 @@ class DefaultAutomaticPersistedQueriesCache : AutomaticPersistedQueriesCache {
} ?: run {
val entry = supplier.invoke()
cache[key] = entry
CompletableFuture.completedFuture(supplier.invoke())
CompletableFuture.completedFuture(entry)
}
}
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@ package com.expediagroup.graphql.generator.internal.types

import com.expediagroup.graphql.generator.SchemaGenerator
import com.expediagroup.graphql.generator.exceptions.InvalidInputFieldTypeException
import com.expediagroup.graphql.generator.internal.extensions.getDeprecationReason
import com.expediagroup.graphql.generator.internal.extensions.getGraphQLDescription
import com.expediagroup.graphql.generator.internal.extensions.getKClass
import com.expediagroup.graphql.generator.internal.extensions.getName
@@ -50,12 +51,15 @@ internal fun generateArgument(generator: SchemaGenerator, parameter: KParameter)
val typeInfo = GraphQLKTypeMetadata(inputType = true, fieldName = parameter.getName(), fieldAnnotations = parameter.annotations)
val graphQLType = generateGraphQLType(generator = generator, type = unwrappedType, typeInfo)

// Deprecation of arguments is currently unsupported: https://youtrack.jetbrains.com/issue/KT-25643
val builder = GraphQLArgument.newArgument()
.name(parameter.getName())
.description(parameter.getGraphQLDescription())
.type(graphQLType.safeCast())

parameter.getDeprecationReason()?.let {
builder.deprecate(it)
}

generateDirectives(generator, parameter, DirectiveLocation.ARGUMENT_DEFINITION).forEach {
builder.withAppliedDirective(it)
}
Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@ import graphql.schema.GraphQLNonNull
import graphql.schema.GraphQLObjectType
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertTrue

@@ -74,6 +75,18 @@ class DirectiveTests {
assertEquals("this query is also deprecated", graphqlDeprecatedQuery.deprecationReason)
}

@Test
fun `SchemaGenerator marks deprecated fields within queries`() {
val schema = toSchema(queries = listOf(TopLevelObject(QueryWithDeprecatedFields())), config = testSchemaConfig())
val topLevelQuery = schema.getObjectType("Query")
val query = topLevelQuery.getFieldDefinition("graphqlQueryWithDeprecatedFields")

assertFalse(query.isDeprecated)
val deprecatedArgument = query.getArgument("something")
assertTrue(deprecatedArgument.isDeprecated)
assertEquals("This field is deprecated", deprecatedArgument.deprecationReason)
}

@Test
fun `Default directive names are normalized`() {
val wiring = object : KotlinSchemaDirectiveWiring {}
@@ -155,6 +168,8 @@ class QueryWithDeprecatedFields {

@Deprecated("this query is also deprecated", replaceWith = ReplaceWith("shinyNewQuery"))
fun graphqlDeprecatedQueryWithReplacement(something: String) = something

fun graphqlQueryWithDeprecatedFields(@GraphQLDeprecated("This field is deprecated") something: String, replacement: String) = "$something -> $replacement"
}

data class ClassWithDeprecatedField(
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@

package com.expediagroup.graphql.generator.internal.types

import com.expediagroup.graphql.generator.annotations.GraphQLDeprecated
import com.expediagroup.graphql.generator.annotations.GraphQLDescription
import com.expediagroup.graphql.generator.annotations.GraphQLName
import com.expediagroup.graphql.generator.exceptions.InvalidInputFieldTypeException
@@ -33,6 +34,7 @@ import kotlin.reflect.full.findParameterByName
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNotNull
import kotlin.test.assertTrue

class GenerateArgumentTest : TypeTestHelper() {

@@ -51,6 +53,8 @@ class GenerateArgumentTest : TypeTestHelper() {

fun changeName(@GraphQLName("newName") input: String) = input

fun deprecate(@GraphQLDeprecated("Deprecated") input: String, replacement: String) = "$input -> $replacement"

fun idClass(idArg: ID) = "Your id is $idArg"

fun interfaceArg(input: MyInterface) = input.id
@@ -108,6 +112,16 @@ class GenerateArgumentTest : TypeTestHelper() {
assertEquals("newName", result.name)
}

@Test
fun `Argument can be deprecated with @GraphqlDeprecated`() {
val kParameter = ArgumentTestClass::deprecate.findParameterByName("input")
assertNotNull(kParameter)
val result = generateArgument(generator, kParameter)

assertTrue(result.isDeprecated, "The argument should be marked as deprecated")
assertEquals("Deprecated", result.deprecationReason, "The deprecation reason should match")
}

@Test
fun `Wrapper ID class argument type is valid`() {
val kParameter = ArgumentTestClass::idClass.findParameterByName("idArg")
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ kotlinx-coroutines = "1.9.0"
# TODO kotlin 1.9 upgrade: fix GraphQLTestUtils and GenerateKotlinxClientIT
kotlinx-serialization = "1.6.3"
ktor = "3.0.3"
fastjson2 = "2.0.53"
fastjson2 = "2.0.56"
maven-plugin-annotation = "3.13.1"
maven-plugin-api = "3.9.8"
maven-project = "2.2.1"
Original file line number Diff line number Diff line change
@@ -32,33 +32,38 @@ import org.openjdk.jmh.annotations.Warmup
import java.util.concurrent.TimeUnit

@State(Scope.Benchmark)
@Fork(value = 5, jvmArgsAppend = ["--add-modules=jdk.incubator.vector", "-Dfastjson2.readerVector=true"])
@Fork(value = 5, jvmArgsAppend = ["--add-modules=jdk.incubator.vector"])
@Warmup(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 4, time = 5, timeUnit = TimeUnit.SECONDS)
open class GraphQLServerRequestBatchDeserializationBenchmark {
private val mapper = jacksonObjectMapper()
private lateinit var request: String
private lateinit var batchRequest: String

@Setup
fun setUp() {
JSON.config(JSONWriter.Feature.WriteNulls)
val loader = this::class.java.classLoader
val operation = loader.getResource("StarWarsDetails.graphql")!!.readText().replace("\n", "\\n")
val variables = loader.getResource("StarWarsDetailsVariables.json")!!.readText()
val operation1 = loader.getResource("StarWarsDetails.graphql")!!.readText().replace("\n", "\\n")
val operation2 = loader.getResource("StarWarsDetails.graphql")!!.readText().replace("\n", "\\n")
val operation3 = loader.getResource("StarWarsDetails.graphql")!!.readText().replace("\n", "\\n")
val operation4 = loader.getResource("StarWarsDetails.graphql")!!.readText().replace("\n", "\\n")
val variables1 = loader.getResource("StarWarsDetailsVariables.json")!!.readText()
val variables2 = loader.getResource("StarWarsDetailsVariables.json")!!.readText()
val variables3 = loader.getResource("StarWarsDetailsVariables.json")!!.readText()
val variables4 = loader.getResource("StarWarsDetailsVariables.json")!!.readText()
batchRequest = """
[
{ "operationName": "StarWarsDetails", "query": "$operation", "variables": $variables },
{ "operationName": "StarWarsDetails", "query": "$operation", "variables": $variables },
{ "operationName": "StarWarsDetails", "query": "$operation", "variables": $variables },
{ "operationName": "StarWarsDetails", "query": "$operation", "variables": $variables }
{ "operationName": "StarWarsDetails", "query": "$operation1", "variables": $variables1 },
{ "operationName": "StarWarsDetails", "query": "$operation2", "variables": $variables2 },
{ "operationName": "StarWarsDetails", "query": "$operation3", "variables": $variables3 },
{ "operationName": "StarWarsDetails", "query": "$operation4", "variables": $variables4 }
]
""".trimIndent()
}

@Benchmark
fun JacksonDeserializeGraphQLBatchRequest(): GraphQLServerRequest = mapper.readValue(batchRequest)
fun jackson(): GraphQLServerRequest = mapper.readValue(batchRequest)

@Benchmark
fun FastJsonDeserializeGraphQLBatchRequest(): GraphQLServerRequest = batchRequest.to<GraphQLServerRequest>()
fun fastjson2(): GraphQLServerRequest = batchRequest.to<GraphQLServerRequest>()
}
Original file line number Diff line number Diff line change
@@ -32,7 +32,7 @@ import org.openjdk.jmh.annotations.Warmup
import java.util.concurrent.TimeUnit

@State(Scope.Benchmark)
@Fork(value = 5, jvmArgsAppend = ["--add-modules=jdk.incubator.vector", "-Dfastjson2.readerVector=true"])
@Fork(value = 5, jvmArgsAppend = ["--add-modules=jdk.incubator.vector"])
@Warmup(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 4, time = 5, timeUnit = TimeUnit.SECONDS)
open class GraphQLServerRequestDeserializationBenchmark {
@@ -55,8 +55,8 @@ open class GraphQLServerRequestDeserializationBenchmark {
}

@Benchmark
fun JacksonDeserializeGraphQLRequest(): GraphQLServerRequest = mapper.readValue(request)
fun jackson(): GraphQLServerRequest = mapper.readValue(request)

@Benchmark
fun FastJsonDeserializeGraphQLRequest(): GraphQLServerRequest = request.to()
fun fastjson2(): GraphQLServerRequest = request.to()
}
Original file line number Diff line number Diff line change
@@ -32,7 +32,7 @@ import org.openjdk.jmh.annotations.Warmup
import java.util.concurrent.TimeUnit

@State(Scope.Benchmark)
@Fork(value = 5, jvmArgsAppend = ["--add-modules=jdk.incubator.vector", "-Dfastjson2.readerVector=true"])
@Fork(value = 5, jvmArgsAppend = ["--add-modules=jdk.incubator.vector"])
@Warmup(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 4, time = 5, timeUnit = TimeUnit.SECONDS)
open class GraphQLServerResponseBatchSerializationBenchmark {
@@ -42,22 +42,35 @@ open class GraphQLServerResponseBatchSerializationBenchmark {
@Setup
fun setUp() {
JSON.config(JSONWriter.Feature.WriteNulls)
val data = mapper.readValue<Map<String, Any?>>(
this::class.java.classLoader.getResourceAsStream("StarWarsDetailsResponse.json")!!
)
batchResponse = GraphQLBatchResponse(
listOf(
GraphQLResponse(data),
GraphQLResponse(data),
GraphQLResponse(data),
GraphQLResponse(data)
GraphQLResponse(
mapper.readValue<Map<String, Any?>>(
this::class.java.classLoader.getResourceAsStream("StarWarsDetailsResponse.json")!!
)
),
GraphQLResponse(
mapper.readValue<Map<String, Any?>>(
this::class.java.classLoader.getResourceAsStream("StarWarsDetailsResponse.json")!!
)
),
GraphQLResponse(
mapper.readValue<Map<String, Any?>>(
this::class.java.classLoader.getResourceAsStream("StarWarsDetailsResponse.json")!!
)
),
GraphQLResponse(
mapper.readValue<Map<String, Any?>>(
this::class.java.classLoader.getResourceAsStream("StarWarsDetailsResponse.json")!!
)
)
)
)
}

@Benchmark
fun JacksonSerializeGraphQLBatchResponse(): String = mapper.writeValueAsString(batchResponse)
fun jackson(): String = mapper.writeValueAsString(batchResponse)

@Benchmark
fun FastJsonSerializeGraphQLBatchResponse(): String = JSON.toJSONString(batchResponse)
fun fastjson2(): String = JSON.toJSONString(batchResponse)
}
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@ import org.openjdk.jmh.annotations.Warmup
import java.util.concurrent.TimeUnit

@State(Scope.Benchmark)
@Fork(value = 5, jvmArgsAppend = ["--add-modules=jdk.incubator.vector", "-Dfastjson2.readerVector=true"])
@Fork(value = 5, jvmArgsAppend = ["--add-modules=jdk.incubator.vector"])
@Warmup(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 4, time = 5, timeUnit = TimeUnit.SECONDS)
open class GraphQLServerResponseSerializationBenchmark {
@@ -41,9 +41,6 @@ open class GraphQLServerResponseSerializationBenchmark {
@Setup
fun setUp() {
JSON.config(JSONWriter.Feature.WriteNulls)
val data = mapper.readValue<Map<String, Any?>>(
this::class.java.classLoader.getResourceAsStream("StarWarsDetailsResponse.json")!!
)
response = GraphQLResponse(
mapper.readValue<Map<String, Any?>>(
this::class.java.classLoader.getResourceAsStream("StarWarsDetailsResponse.json")!!
@@ -52,8 +49,8 @@ open class GraphQLServerResponseSerializationBenchmark {
}

@Benchmark
fun JacksonSerializeGraphQLResponse(): String = mapper.writeValueAsString(response)
fun jackson(): String = mapper.writeValueAsString(response)

@Benchmark
fun FastJsonSerializeGraphQLResponse(): String = JSON.toJSONString(response)
fun fastjson2(): String = JSON.toJSONString(response)
}
Loading