@@ -20,6 +20,11 @@ package com.netflix.graphql.dgs.codegen
2020
2121import com.netflix.graphql.dgs.codegen.generators.java.*
2222import com.netflix.graphql.dgs.codegen.generators.kotlin.*
23+ import com.netflix.graphql.dgs.codegen.generators.kotlin2.generateKotlin2ClientTypes
24+ import com.netflix.graphql.dgs.codegen.generators.kotlin2.generateKotlin2DataTypes
25+ import com.netflix.graphql.dgs.codegen.generators.kotlin2.generateKotlin2EnumTypes
26+ import com.netflix.graphql.dgs.codegen.generators.kotlin2.generateKotlin2InputTypes
27+ import com.netflix.graphql.dgs.codegen.generators.kotlin2.generateKotlin2Interfaces
2328import com.netflix.graphql.dgs.codegen.generators.shared.SchemaExtensionsUtils.findEnumExtensions
2429import com.netflix.graphql.dgs.codegen.generators.shared.SchemaExtensionsUtils.findInputExtensions
2530import com.netflix.graphql.dgs.codegen.generators.shared.SchemaExtensionsUtils.findInterfaceExtensions
@@ -32,6 +37,7 @@ import graphql.language.*
3237import graphql.parser.MultiSourceReader
3338import graphql.parser.Parser
3439import graphql.parser.ParserOptions
40+ import graphql.schema.idl.TypeUtil
3541import java.io.File
3642import java.nio.file.Path
3743import java.nio.file.Paths
@@ -73,10 +79,12 @@ class CodeGen(private val config: CodeGenConfig) {
7379 }
7480 codeGenResult.javaConstants.forEach { it.writeTo(config.outputDir) }
7581 codeGenResult.kotlinDataTypes.forEach { it.writeTo(config.outputDir) }
82+ codeGenResult.kotlinInputTypes.forEach { it.writeTo(config.outputDir) }
7683 codeGenResult.kotlinInterfaces.forEach { it.writeTo(config.outputDir) }
7784 codeGenResult.kotlinEnumTypes.forEach { it.writeTo(config.outputDir) }
7885 codeGenResult.kotlinDataFetchers.forEach { it.writeTo(config.examplesOutputDir) }
7986 codeGenResult.kotlinConstants.forEach { it.writeTo(config.outputDir) }
87+ codeGenResult.kotlinClientTypes.forEach { it.writeTo(config.outputDir) }
8088 }
8189
8290 return codeGenResult
@@ -105,9 +113,50 @@ class CodeGen(private val config: CodeGenConfig) {
105113 readerBuilder.string(schema, null )
106114 }
107115
108- return readerBuilder.build().use { reader ->
116+ val document = readerBuilder.build().use { reader ->
109117 parser.parseDocument(reader, options)
110118 }
119+
120+ return document.transform {
121+
122+ // for kotlin2, add implicit types like PageInfo to the schema so classes are generated
123+ if (config.generateKotlinNullableClasses || config.generateKotlinClosureProjections) {
124+ val objectTypeDefs = document.getDefinitionsOfType(ObjectTypeDefinition ::class .java)
125+ if (! objectTypeDefs.any { def -> def.name == " PageInfo" } &&
126+ objectTypeDefs.any { def -> def.fieldDefinitions.any { field -> TypeUtil .unwrapAll(field.type).name == " PageInfo" } }
127+ ) {
128+ it.definition(
129+ ObjectTypeDefinition .newObjectTypeDefinition()
130+ .name(" PageInfo" )
131+ .fieldDefinition(
132+ FieldDefinition .newFieldDefinition()
133+ .name(" hasNextPage" )
134+ .type(NonNullType (TypeName (" Boolean" )))
135+ .build()
136+ )
137+ .fieldDefinition(
138+ FieldDefinition .newFieldDefinition()
139+ .name(" hasPreviousPage" )
140+ .type(NonNullType (TypeName (" Boolean" )))
141+ .build()
142+ )
143+ .fieldDefinition(
144+ FieldDefinition .newFieldDefinition()
145+ .name(" startCursor" )
146+ .type(TypeName (" String" ))
147+ .build()
148+ )
149+ .fieldDefinition(
150+ FieldDefinition .newFieldDefinition()
151+ .name(" endCursor" )
152+ .type(TypeName (" String" ))
153+ .build()
154+ )
155+ .build()
156+ )
157+ }
158+ }
159+ }
111160 }
112161
113162 private fun generateJava (): CodeGenResult {
@@ -239,37 +288,72 @@ class CodeGen(private val config: CodeGenConfig) {
239288 private fun generateKotlin (): CodeGenResult {
240289 val definitions = document.definitions
241290
242- val datatypesResult = generateKotlinDataTypes(definitions)
243- val inputTypes = generateKotlinInputTypes(definitions)
244- val interfacesResult = generateKotlinInterfaceTypes(definitions)
291+ val requiredTypeCollector = RequiredTypeCollector (
292+ document = document,
293+ queries = config.includeQueries,
294+ mutations = config.includeMutations,
295+ subscriptions = config.includeSubscriptions,
296+ )
297+ val requiredTypes = requiredTypeCollector.requiredTypes
245298
246- val unionResult = definitions.asSequence()
247- .filterIsInstance<UnionTypeDefinition >()
248- .excludeSchemaTypeExtension()
249- .map {
250- val extensions = findUnionExtensions(it.name, definitions)
251- KotlinUnionTypeGenerator (config).generate(it, extensions)
252- }
253- .fold(CodeGenResult ()) { t: CodeGenResult , u: CodeGenResult -> t.merge(u) }
299+ val dataTypes = if (config.generateKotlinNullableClasses) {
254300
255- val enumsResult = definitions.asSequence()
256- .filterIsInstance<EnumTypeDefinition >()
257- .excludeSchemaTypeExtension()
258- .filter { config.generateDataTypes || it.name in requiredTypeCollector.requiredTypes }
259- .map {
260- val extensions = findEnumExtensions(it.name, definitions)
261- KotlinEnumTypeGenerator (config).generate(it, extensions)
262- }
263- .fold(CodeGenResult ()) { t: CodeGenResult , u: CodeGenResult -> t.merge(u) }
301+ CodeGenResult (
302+ kotlinDataTypes = generateKotlin2DataTypes(config, document, requiredTypes),
303+ kotlinInputTypes = generateKotlin2InputTypes(config, document, requiredTypes),
304+ kotlinInterfaces = generateKotlin2Interfaces(config, document),
305+ kotlinEnumTypes = generateKotlin2EnumTypes(config, document, requiredTypes),
306+ kotlinConstants = KotlinConstantsGenerator (config, document).generate().kotlinConstants,
307+ )
308+ } else {
264309
265- val constantsClass = KotlinConstantsGenerator (config, document).generate()
310+ val datatypesResult = generateKotlinDataTypes(definitions)
311+ val inputTypes = generateKotlinInputTypes(definitions)
312+ val interfacesResult = generateKotlinInterfaceTypes(definitions)
266313
267- val client = generateJavaClientApi(definitions)
268- val entitiesClient = generateJavaClientEntitiesApi(definitions)
269- val entitiesRepresentationsTypes = generateKotlinClientEntitiesRepresentations(definitions)
314+ val unionResult = definitions.asSequence()
315+ .filterIsInstance<UnionTypeDefinition >()
316+ .excludeSchemaTypeExtension()
317+ .map {
318+ val extensions = findUnionExtensions(it.name, definitions)
319+ KotlinUnionTypeGenerator (config).generate(it, extensions)
320+ }
321+ .fold(CodeGenResult ()) { t: CodeGenResult , u: CodeGenResult -> t.merge(u) }
322+
323+ val enumsResult = definitions.asSequence()
324+ .filterIsInstance<EnumTypeDefinition >()
325+ .excludeSchemaTypeExtension()
326+ .filter { config.generateDataTypes || it.name in requiredTypeCollector.requiredTypes }
327+ .map {
328+ val extensions = findEnumExtensions(it.name, definitions)
329+ KotlinEnumTypeGenerator (config).generate(it, extensions)
330+ }
331+ .fold(CodeGenResult ()) { t: CodeGenResult , u: CodeGenResult -> t.merge(u) }
332+
333+ val constantsClass = KotlinConstantsGenerator (config, document).generate()
334+
335+ datatypesResult
336+ .merge(inputTypes)
337+ .merge(interfacesResult)
338+ .merge(unionResult)
339+ .merge(enumsResult)
340+ .merge(constantsClass)
341+ }
270342
271- return datatypesResult.merge(inputTypes).merge(interfacesResult).merge(unionResult).merge(enumsResult)
272- .merge(client).merge(entitiesClient).merge(entitiesRepresentationsTypes).merge(constantsClass)
343+ val clientTypes = if (config.generateKotlinClosureProjections) {
344+ CodeGenResult (
345+ kotlinClientTypes = generateKotlin2ClientTypes(config, document),
346+ )
347+ } else {
348+
349+ val client = generateJavaClientApi(definitions)
350+ val entitiesClient = generateJavaClientEntitiesApi(definitions)
351+ val entitiesRepresentationsTypes = generateKotlinClientEntitiesRepresentations(definitions)
352+
353+ client.merge(entitiesClient).merge(entitiesRepresentationsTypes)
354+ }
355+
356+ return dataTypes.merge(clientTypes)
273357 }
274358
275359 private fun generateKotlinClientEntitiesRepresentations (definitions : Collection <Definition <* >>): CodeGenResult {
@@ -354,6 +438,8 @@ data class CodeGenConfig(
354438 val generateBoxedTypes : Boolean = false ,
355439 val generateClientApi : Boolean = false ,
356440 val generateInterfaces : Boolean = false ,
441+ val generateKotlinNullableClasses : Boolean = false ,
442+ val generateKotlinClosureProjections : Boolean = false ,
357443 val typeMapping : Map <String , String > = emptyMap(),
358444 val includeQueries : Set <String > = emptySet(),
359445 val includeMutations : Set <String > = emptySet(),
@@ -398,7 +484,7 @@ data class CodeGenConfig(
398484
399485enum class Language {
400486 JAVA ,
401- KOTLIN
487+ KOTLIN ,
402488}
403489
404490data class CodeGenResult (
@@ -410,10 +496,12 @@ data class CodeGenResult(
410496 val clientProjections : List <JavaFile > = listOf(),
411497 val javaConstants : List <JavaFile > = listOf(),
412498 val kotlinDataTypes : List <FileSpec > = listOf(),
499+ val kotlinInputTypes : List <FileSpec > = listOf(),
413500 val kotlinInterfaces : List <FileSpec > = listOf(),
414501 val kotlinEnumTypes : List <FileSpec > = listOf(),
415502 val kotlinDataFetchers : List <FileSpec > = listOf(),
416- val kotlinConstants : List <FileSpec > = emptyList()
503+ val kotlinConstants : List <FileSpec > = listOf(),
504+ val kotlinClientTypes : List <FileSpec > = listOf(),
417505) {
418506 fun merge (current : CodeGenResult ): CodeGenResult {
419507 val javaDataTypes = this .javaDataTypes.plus(current.javaDataTypes)
@@ -424,10 +512,12 @@ data class CodeGenResult(
424512 val clientProjections = this .clientProjections.plus(current.clientProjections)
425513 val javaConstants = this .javaConstants.plus(current.javaConstants)
426514 val kotlinDataTypes = this .kotlinDataTypes.plus(current.kotlinDataTypes)
515+ val kotlinInputTypes = this .kotlinInputTypes.plus(current.kotlinInputTypes)
427516 val kotlinInterfaces = this .kotlinInterfaces.plus(current.kotlinInterfaces)
428517 val kotlinEnumTypes = this .kotlinEnumTypes.plus(current.kotlinEnumTypes)
429518 val kotlinDataFetchers = this .kotlinDataFetchers.plus(current.kotlinDataFetchers)
430519 val kotlinConstants = this .kotlinConstants.plus(current.kotlinConstants)
520+ val kotlinClientTypes = this .kotlinClientTypes.plus(current.kotlinClientTypes)
431521
432522 return CodeGenResult (
433523 javaDataTypes = javaDataTypes,
@@ -438,32 +528,32 @@ data class CodeGenResult(
438528 clientProjections = clientProjections,
439529 javaConstants = javaConstants,
440530 kotlinDataTypes = kotlinDataTypes,
531+ kotlinInputTypes = kotlinInputTypes,
441532 kotlinInterfaces = kotlinInterfaces,
442533 kotlinEnumTypes = kotlinEnumTypes,
443534 kotlinDataFetchers = kotlinDataFetchers,
444- kotlinConstants = kotlinConstants
535+ kotlinConstants = kotlinConstants,
536+ kotlinClientTypes = kotlinClientTypes,
445537 )
446538 }
447539
448540 fun javaSources (): List <JavaFile > {
449541 return javaDataTypes
450- .asSequence()
451542 .plus(javaInterfaces)
452543 .plus(javaEnumTypes)
453544 .plus(javaDataFetchers)
454545 .plus(javaQueryTypes)
455546 .plus(clientProjections)
456547 .plus(javaConstants)
457- .toList()
458548 }
459549
460550 fun kotlinSources (): List <FileSpec > {
461551 return kotlinDataTypes
462- .asSequence( )
552+ .plus(kotlinInputTypes )
463553 .plus(kotlinInterfaces)
464554 .plus(kotlinEnumTypes)
465555 .plus(kotlinConstants)
466- .toList( )
556+ .plus(kotlinClientTypes )
467557 }
468558}
469559
@@ -498,21 +588,10 @@ fun List<FieldDefinition>.filterIncludedInConfig(definitionName: String, config:
498588 }
499589}
500590
501- fun ObjectTypeDefinition.shouldSkip (config : CodeGenConfig ): Boolean = shouldSkip(this , config)
502-
503- fun InputObjectTypeDefinition.shouldSkip (config : CodeGenConfig ): Boolean = shouldSkip(this , config)
504-
505- fun InterfaceTypeDefinition.shouldSkip (config : CodeGenConfig ): Boolean = shouldSkip(this , config)
506-
507- fun UnionTypeDefinition.shouldSkip (config : CodeGenConfig ): Boolean = shouldSkip(this , config)
508-
509- fun EnumTypeDefinition.shouldSkip (config : CodeGenConfig ): Boolean = shouldSkip(this , config)
510-
511- private fun <T : DirectivesContainer <* >> shouldSkip (
512- typeDefinition : DirectivesContainer <T >,
591+ fun <T : DirectivesContainer <* >> DirectivesContainer<T>.shouldSkip (
513592 config : CodeGenConfig
514593): Boolean {
515- return typeDefinition. directives.any { it.name == " skipcodegen" } || config.typeMapping.containsKey((typeDefinition as NamedNode <* >).name)
594+ return directives.any { it.name == " skipcodegen" } || config.typeMapping.containsKey((this as NamedNode <* >).name)
516595}
517596
518597fun TypeDefinition <* >.fieldDefinitions (): List <FieldDefinition > {
0 commit comments