Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit2959ce9

Browse files
Devin/1758753881 shared response types (#2136)
### 📝 Description### 🔗 Related Issuesarthurkkp-cog#7---------Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parente3b74bf commit2959ce9

File tree

17 files changed

+380
-9
lines changed

17 files changed

+380
-9
lines changed

‎plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generateClient.kt‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ fun generateClient(
3535
schemaPath:String,
3636
queries:List<File>,
3737
useOptionalInputWrapper:Boolean = false,
38-
parserOptions:ParserOptions.Builder.()->Unit = {}
38+
parserOptions:ParserOptions.Builder.()->Unit = {},
39+
useSharedResponseTypes:Boolean = false
3940
):List<FileSpec> {
4041
val customScalars= customScalarsMap.associateBy { it.scalar }
4142
val config=GraphQLClientGeneratorConfig(
@@ -44,7 +45,8 @@ fun generateClient(
4445
customScalarMap= customScalars,
4546
serializer= serializer,
4647
useOptionalInputWrapper= useOptionalInputWrapper,
47-
parserOptions= parserOptions
48+
parserOptions= parserOptions,
49+
useSharedResponseTypes= useSharedResponseTypes
4850
)
4951
val generator=GraphQLClientGenerator(schemaPath, config)
5052
return generator.generate(queries)

‎plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/GraphQLClientGenerator.kt‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ class GraphQLClientGenerator(
6666
*/
6767
fungenerate(queries:List<File>):List<FileSpec> {
6868
val result= mutableListOf<FileSpec>()
69+
70+
// Generate client code with shared types
6971
for (queryin queries) {
7072
result.addAll(generate(query))
7173
}
@@ -119,8 +121,10 @@ class GraphQLClientGenerator(
119121
allowDeprecated= config.allowDeprecated,
120122
customScalarMap= config.customScalarMap,
121123
serializer= config.serializer,
122-
useOptionalInputWrapper= config.useOptionalInputWrapper
124+
useOptionalInputWrapper= config.useOptionalInputWrapper,
125+
config= config
123126
)
127+
124128
val queryConstName= capitalizedOperationName.toUpperUnderscore()
125129
val queryConstProp=PropertySpec.builder(queryConstName,STRING)
126130
.addModifiers(KModifier.CONST)
@@ -216,6 +220,7 @@ class GraphQLClientGenerator(
216220
// shared types
217221
sharedTypes.putAll(context.enumClassToTypeSpecs.mapValues {listOf(it.value) })
218222
sharedTypes.putAll(context.inputClassToTypeSpecs.mapValues {listOf(it.value) })
223+
sharedTypes.putAll(context.responseClassToTypeSpecs.mapValues {listOf(it.value) })
219224
context.scalarClassToConverterTypeSpecs
220225
.values
221226
.forEach {

‎plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/GraphQLClientGeneratorConfig.kt‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ data class GraphQLClientGeneratorConfig(
3333
valserializer:GraphQLSerializer =GraphQLSerializer.JACKSON,
3434
/** Explicit opt-in flag to enable support for optional inputs.*/
3535
valuseOptionalInputWrapper:Boolean =false,
36+
/** Boolean flag indicating whether to generate shared response types instead of operation-specific duplicates. Defaults to false.*/
37+
valuseSharedResponseTypes:Boolean =false,
3638
/** Set parser options for processing GraphQL queries and schema definition language documents*/
3739
valparserOptions:ParserOptions.Builder.()->Unit = {}
3840
)

‎plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/GraphQLClientGeneratorContext.kt‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import com.squareup.kotlinpoet.TypeAliasSpec
2121
importcom.squareup.kotlinpoet.TypeName
2222
importcom.squareup.kotlinpoet.TypeSpec
2323
importgraphql.language.Document
24+
importgraphql.language.Selection
2425
importgraphql.schema.idl.TypeDefinitionRegistry
2526

2627
/**
@@ -36,7 +37,8 @@ data class GraphQLClientGeneratorContext(
3637
valallowDeprecated:Boolean =false,
3738
valcustomScalarMap:Map<String,GraphQLScalar> = mapOf(),
3839
valserializer:GraphQLSerializer =GraphQLSerializer.JACKSON,
39-
valuseOptionalInputWrapper:Boolean =false
40+
valuseOptionalInputWrapper:Boolean =false,
41+
valconfig:GraphQLClientGeneratorConfig
4042
) {
4143
// per operation caches
4244
val typeSpecs:MutableMap<ClassName,TypeSpec>=mutableMapOf()
@@ -45,13 +47,16 @@ data class GraphQLClientGeneratorContext(
4547
// shared type caches
4648
val enumClassToTypeSpecs:MutableMap<ClassName,TypeSpec>=mutableMapOf()
4749
val inputClassToTypeSpecs:MutableMap<ClassName,TypeSpec>=mutableMapOf()
50+
val responseClassToTypeSpecs:MutableMap<ClassName,TypeSpec>=mutableMapOf()
4851
val scalarClassToConverterTypeSpecs:MutableMap<ClassName,ScalarConverterInfo>=mutableMapOf()
4952
val typeAliases:MutableMap<String,TypeAliasSpec>=mutableMapOf()
5053
internalfunisTypeAlias(typeName:String)= typeAliases.containsKey(typeName)
5154

5255
// class name and type selection caches
5356
val classNameCache:MutableMap<String,MutableList<ClassName>>=mutableMapOf()
5457
val typeToSelectionSetMap:MutableMap<String,Set<String>>=mutableMapOf()
58+
val responseTypeToSelectionSetMap:MutableMap<String,MutableSet<Selection<*>>>=mutableMapOf()
59+
val sharedTypeVariantToSelectionSetMap:MutableMap<String,Set<String>>=mutableMapOf()
5560

5661
privateval customScalarClassNames:Set<ClassName>= customScalarMap.values.map { it.className }.toSet()
5762
internalfunisCustomScalar(typeName:TypeName):Boolean= customScalarClassNames.contains(typeName)

‎plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/types/generateTypeName.kt‎

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,50 @@ internal fun generateCustomClassName(context: GraphQLClientGeneratorContext, gra
111111
// generate corresponding type spec
112112
when (graphQLTypeDefinition) {
113113
isObjectTypeDefinition-> {
114-
className= generateClassName(context, graphQLTypeDefinition, selectionSet)
115-
context.typeSpecs[className]= generateGraphQLObjectTypeSpec(context, graphQLTypeDefinition, selectionSet)
114+
if (context.config.useSharedResponseTypes) {
115+
// Use cross-operation reuse logic similar to existing single-operation logic
116+
val globalCachedTypes= context.responseClassToTypeSpecs.keys.filter { it.simpleName.startsWith(graphQLTypeDefinition.name) }
117+
118+
if (globalCachedTypes.isNotEmpty()) {
119+
// Check if any existing shared type matches this selection set
120+
var foundMatch=false
121+
for (cachedTypein globalCachedTypes) {
122+
if (isCachedTypeApplicableForSharedType(context, cachedType, graphQLTypeDefinition, selectionSet)) {
123+
className= cachedType
124+
foundMatch=true
125+
break
126+
}
127+
}
128+
129+
if (!foundMatch) {
130+
// Generate new variant (ComplexObject2, ComplexObject3, etc.)
131+
val variantNumber= globalCachedTypes.size+1
132+
val variantName=if (variantNumber==1) graphQLTypeDefinition.nameelse"${graphQLTypeDefinition.name}$variantNumber"
133+
className=ClassName("${context.packageName}.responses", variantName)
134+
context.responseClassToTypeSpecs[className]= generateGraphQLObjectTypeSpec(context, graphQLTypeDefinition, selectionSet, variantName)
135+
136+
// Track selection set for this variant
137+
if (selectionSet!=null) {
138+
val selectedFields= calculateSelectedFields(context, graphQLTypeDefinition.name, selectionSet)
139+
context.sharedTypeVariantToSelectionSetMap[variantName]= selectedFields
140+
}
141+
}
142+
}else {
143+
// First occurrence - create base shared type
144+
className=ClassName("${context.packageName}.responses", graphQLTypeDefinition.name)
145+
context.responseClassToTypeSpecs[className]= generateGraphQLObjectTypeSpec(context, graphQLTypeDefinition, selectionSet)
146+
147+
// Track selection set for this variant
148+
if (selectionSet!=null) {
149+
val selectedFields= calculateSelectedFields(context, graphQLTypeDefinition.name, selectionSet)
150+
context.sharedTypeVariantToSelectionSetMap[graphQLTypeDefinition.name]= selectedFields
151+
}
152+
}
153+
}else {
154+
// Use original logic for operation-specific types
155+
className= generateClassName(context, graphQLTypeDefinition, selectionSet)
156+
context.typeSpecs[className]= generateGraphQLObjectTypeSpec(context, graphQLTypeDefinition, selectionSet)
157+
}
116158
}
117159
isInputObjectTypeDefinition-> {
118160
className= generateClassName(context, graphQLTypeDefinition, selectionSet, packageName="${context.packageName}.inputs")
@@ -258,3 +300,19 @@ private fun calculateSelectedFields(
258300
}
259301
return result
260302
}
303+
304+
/**
305+
* Helper function to check if a cached shared type matches the current selection set.
306+
*/
307+
privatefunisCachedTypeApplicableForSharedType(
308+
context:GraphQLClientGeneratorContext,
309+
cachedClassName:ClassName,
310+
graphQLTypeDefinition:TypeDefinition<*>,
311+
selectionSet:SelectionSet?
312+
):Boolean {
313+
if (selectionSet==null)returntrue
314+
315+
val selectedFields= calculateSelectedFields(context, graphQLTypeDefinition.name, selectionSet)
316+
val cachedTypeFields= context.sharedTypeVariantToSelectionSetMap[cachedClassName.simpleName]
317+
return selectedFields== cachedTypeFields
318+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
queryOperation1 {
2+
first:complexObjectQuery {
3+
id
4+
name
5+
}
6+
second:complexObjectQuery {
7+
id
8+
name
9+
details {
10+
id
11+
value
12+
}
13+
}
14+
third:complexObjectQuery {
15+
id
16+
name
17+
details {
18+
id
19+
}
20+
}
21+
fourth:complexObjectQuery {
22+
id
23+
name
24+
}
25+
fifth:complexObjectQuery {
26+
id
27+
name
28+
details {
29+
id
30+
value
31+
}
32+
}
33+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
packagecom.expediagroup.graphql.generated
2+
3+
importcom.expediagroup.graphql.client.Generated
4+
importcom.expediagroup.graphql.client.types.GraphQLClientRequest
5+
importcom.expediagroup.graphql.generated.responses.ComplexObject
6+
importcom.expediagroup.graphql.generated.responses.ComplexObject2
7+
importcom.expediagroup.graphql.generated.responses.ComplexObject3
8+
importcom.fasterxml.jackson.`annotation`.JsonProperty
9+
importkotlin.String
10+
importkotlin.reflect.KClass
11+
12+
publicconstvalOPERATION1:String=
13+
"query Operation1 {\n first: complexObjectQuery {\n id\n name\n }\n second: complexObjectQuery {\n id\n name\n details {\n id\n value\n }\n }\n third: complexObjectQuery {\n id\n name\n details {\n id\n }\n }\n fourth: complexObjectQuery {\n id\n name\n }\n fifth: complexObjectQuery {\n id\n name\n details {\n id\n value\n }\n }\n}"
14+
15+
@Generated
16+
publicclassOperation1 :GraphQLClientRequest<Operation1.Result> {
17+
overrideval query:String=OPERATION1
18+
19+
overrideval operationName:String="Operation1"
20+
21+
overridefunresponseType():KClass<Operation1.Result>=Operation1.Result::class
22+
23+
@Generated
24+
publicdata classResult(
25+
/**
26+
* Query returning an object that references another object
27+
*/
28+
@get:JsonProperty(value = "first")
29+
publicvalfirst:ComplexObject,
30+
/**
31+
* Query returning an object that references another object
32+
*/
33+
@get:JsonProperty(value = "second")
34+
publicvalsecond:ComplexObject2,
35+
/**
36+
* Query returning an object that references another object
37+
*/
38+
@get:JsonProperty(value = "third")
39+
publicvalthird:ComplexObject3,
40+
/**
41+
* Query returning an object that references another object
42+
*/
43+
@get:JsonProperty(value = "fourth")
44+
publicvalfourth:ComplexObject,
45+
/**
46+
* Query returning an object that references another object
47+
*/
48+
@get:JsonProperty(value = "fifth")
49+
publicvalfifth:ComplexObject2,
50+
)
51+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
queryOperation2 {
2+
first:complexObjectQuery {
3+
id
4+
name
5+
}
6+
second:complexObjectQuery {
7+
id
8+
name
9+
details {
10+
id
11+
value
12+
}
13+
}
14+
third:complexObjectQuery {
15+
id
16+
name
17+
details {
18+
id
19+
}
20+
}
21+
fourth:complexObjectQuery {
22+
id
23+
name
24+
}
25+
fifth:complexObjectQuery {
26+
id
27+
name
28+
details {
29+
id
30+
value
31+
}
32+
}
33+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
packagecom.expediagroup.graphql.generated
2+
3+
importcom.expediagroup.graphql.client.Generated
4+
importcom.expediagroup.graphql.client.types.GraphQLClientRequest
5+
importcom.expediagroup.graphql.generated.responses.ComplexObject
6+
importcom.expediagroup.graphql.generated.responses.ComplexObject2
7+
importcom.expediagroup.graphql.generated.responses.ComplexObject3
8+
importcom.fasterxml.jackson.`annotation`.JsonProperty
9+
importkotlin.String
10+
importkotlin.reflect.KClass
11+
12+
publicconstvalOPERATION2:String=
13+
"query Operation2 {\n first: complexObjectQuery {\n id\n name\n }\n second: complexObjectQuery {\n id\n name\n details {\n id\n value\n }\n }\n third: complexObjectQuery {\n id\n name\n details {\n id\n }\n }\n fourth: complexObjectQuery {\n id\n name\n }\n fifth: complexObjectQuery {\n id\n name\n details {\n id\n value\n }\n }\n}"
14+
15+
@Generated
16+
publicclassOperation2 :GraphQLClientRequest<Operation2.Result> {
17+
overrideval query:String=OPERATION2
18+
19+
overrideval operationName:String="Operation2"
20+
21+
overridefunresponseType():KClass<Operation2.Result>=Operation2.Result::class
22+
23+
@Generated
24+
publicdata classResult(
25+
/**
26+
* Query returning an object that references another object
27+
*/
28+
@get:JsonProperty(value = "first")
29+
publicvalfirst:ComplexObject,
30+
/**
31+
* Query returning an object that references another object
32+
*/
33+
@get:JsonProperty(value = "second")
34+
publicvalsecond:ComplexObject2,
35+
/**
36+
* Query returning an object that references another object
37+
*/
38+
@get:JsonProperty(value = "third")
39+
publicvalthird:ComplexObject3,
40+
/**
41+
* Query returning an object that references another object
42+
*/
43+
@get:JsonProperty(value = "fourth")
44+
publicvalfourth:ComplexObject,
45+
/**
46+
* Query returning an object that references another object
47+
*/
48+
@get:JsonProperty(value = "fifth")
49+
publicvalfifth:ComplexObject2,
50+
)
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
packagecom.expediagroup.graphql.generated.responses
2+
3+
importcom.expediagroup.graphql.client.Generated
4+
importcom.fasterxml.jackson.`annotation`.JsonProperty
5+
importkotlin.Int
6+
importkotlin.String
7+
8+
/**
9+
* Multi line description of a complex type.
10+
* This is a second line of the paragraph.
11+
* This is final line of the description.
12+
*/
13+
@Generated
14+
publicdata classComplexObject(
15+
/**
16+
* Some unique identifier
17+
*/
18+
@get:JsonProperty(value = "id")
19+
publicvalid:Int,
20+
/**
21+
* Some object name
22+
*/
23+
@get:JsonProperty(value = "name")
24+
publicvalname:String,
25+
)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp