@@ -20,6 +20,11 @@ package com.netflix.graphql.dgs.codegen
20
20
21
21
import com.netflix.graphql.dgs.codegen.generators.java.*
22
22
import 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
23
28
import com.netflix.graphql.dgs.codegen.generators.shared.SchemaExtensionsUtils.findEnumExtensions
24
29
import com.netflix.graphql.dgs.codegen.generators.shared.SchemaExtensionsUtils.findInputExtensions
25
30
import com.netflix.graphql.dgs.codegen.generators.shared.SchemaExtensionsUtils.findInterfaceExtensions
@@ -32,6 +37,7 @@ import graphql.language.*
32
37
import graphql.parser.MultiSourceReader
33
38
import graphql.parser.Parser
34
39
import graphql.parser.ParserOptions
40
+ import graphql.schema.idl.TypeUtil
35
41
import java.io.File
36
42
import java.nio.file.Path
37
43
import java.nio.file.Paths
@@ -73,10 +79,12 @@ class CodeGen(private val config: CodeGenConfig) {
73
79
}
74
80
codeGenResult.javaConstants.forEach { it.writeTo(config.outputDir) }
75
81
codeGenResult.kotlinDataTypes.forEach { it.writeTo(config.outputDir) }
82
+ codeGenResult.kotlinInputTypes.forEach { it.writeTo(config.outputDir) }
76
83
codeGenResult.kotlinInterfaces.forEach { it.writeTo(config.outputDir) }
77
84
codeGenResult.kotlinEnumTypes.forEach { it.writeTo(config.outputDir) }
78
85
codeGenResult.kotlinDataFetchers.forEach { it.writeTo(config.examplesOutputDir) }
79
86
codeGenResult.kotlinConstants.forEach { it.writeTo(config.outputDir) }
87
+ codeGenResult.kotlinClientTypes.forEach { it.writeTo(config.outputDir) }
80
88
}
81
89
82
90
return codeGenResult
@@ -105,9 +113,50 @@ class CodeGen(private val config: CodeGenConfig) {
105
113
readerBuilder.string(schema, null )
106
114
}
107
115
108
- return readerBuilder.build().use { reader ->
116
+ val document = readerBuilder.build().use { reader ->
109
117
parser.parseDocument(reader, options)
110
118
}
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
+ }
111
160
}
112
161
113
162
private fun generateJava (): CodeGenResult {
@@ -239,37 +288,72 @@ class CodeGen(private val config: CodeGenConfig) {
239
288
private fun generateKotlin (): CodeGenResult {
240
289
val definitions = document.definitions
241
290
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
245
298
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) {
254
300
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 {
264
309
265
- val constantsClass = KotlinConstantsGenerator (config, document).generate()
310
+ val datatypesResult = generateKotlinDataTypes(definitions)
311
+ val inputTypes = generateKotlinInputTypes(definitions)
312
+ val interfacesResult = generateKotlinInterfaceTypes(definitions)
266
313
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
+ }
270
342
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)
273
357
}
274
358
275
359
private fun generateKotlinClientEntitiesRepresentations (definitions : Collection <Definition <* >>): CodeGenResult {
@@ -354,6 +438,8 @@ data class CodeGenConfig(
354
438
val generateBoxedTypes : Boolean = false ,
355
439
val generateClientApi : Boolean = false ,
356
440
val generateInterfaces : Boolean = false ,
441
+ val generateKotlinNullableClasses : Boolean = false ,
442
+ val generateKotlinClosureProjections : Boolean = false ,
357
443
val typeMapping : Map <String , String > = emptyMap(),
358
444
val includeQueries : Set <String > = emptySet(),
359
445
val includeMutations : Set <String > = emptySet(),
@@ -398,7 +484,7 @@ data class CodeGenConfig(
398
484
399
485
enum class Language {
400
486
JAVA ,
401
- KOTLIN
487
+ KOTLIN ,
402
488
}
403
489
404
490
data class CodeGenResult (
@@ -410,10 +496,12 @@ data class CodeGenResult(
410
496
val clientProjections : List <JavaFile > = listOf(),
411
497
val javaConstants : List <JavaFile > = listOf(),
412
498
val kotlinDataTypes : List <FileSpec > = listOf(),
499
+ val kotlinInputTypes : List <FileSpec > = listOf(),
413
500
val kotlinInterfaces : List <FileSpec > = listOf(),
414
501
val kotlinEnumTypes : List <FileSpec > = listOf(),
415
502
val kotlinDataFetchers : List <FileSpec > = listOf(),
416
- val kotlinConstants : List <FileSpec > = emptyList()
503
+ val kotlinConstants : List <FileSpec > = listOf(),
504
+ val kotlinClientTypes : List <FileSpec > = listOf(),
417
505
) {
418
506
fun merge (current : CodeGenResult ): CodeGenResult {
419
507
val javaDataTypes = this .javaDataTypes.plus(current.javaDataTypes)
@@ -424,10 +512,12 @@ data class CodeGenResult(
424
512
val clientProjections = this .clientProjections.plus(current.clientProjections)
425
513
val javaConstants = this .javaConstants.plus(current.javaConstants)
426
514
val kotlinDataTypes = this .kotlinDataTypes.plus(current.kotlinDataTypes)
515
+ val kotlinInputTypes = this .kotlinInputTypes.plus(current.kotlinInputTypes)
427
516
val kotlinInterfaces = this .kotlinInterfaces.plus(current.kotlinInterfaces)
428
517
val kotlinEnumTypes = this .kotlinEnumTypes.plus(current.kotlinEnumTypes)
429
518
val kotlinDataFetchers = this .kotlinDataFetchers.plus(current.kotlinDataFetchers)
430
519
val kotlinConstants = this .kotlinConstants.plus(current.kotlinConstants)
520
+ val kotlinClientTypes = this .kotlinClientTypes.plus(current.kotlinClientTypes)
431
521
432
522
return CodeGenResult (
433
523
javaDataTypes = javaDataTypes,
@@ -438,32 +528,32 @@ data class CodeGenResult(
438
528
clientProjections = clientProjections,
439
529
javaConstants = javaConstants,
440
530
kotlinDataTypes = kotlinDataTypes,
531
+ kotlinInputTypes = kotlinInputTypes,
441
532
kotlinInterfaces = kotlinInterfaces,
442
533
kotlinEnumTypes = kotlinEnumTypes,
443
534
kotlinDataFetchers = kotlinDataFetchers,
444
- kotlinConstants = kotlinConstants
535
+ kotlinConstants = kotlinConstants,
536
+ kotlinClientTypes = kotlinClientTypes,
445
537
)
446
538
}
447
539
448
540
fun javaSources (): List <JavaFile > {
449
541
return javaDataTypes
450
- .asSequence()
451
542
.plus(javaInterfaces)
452
543
.plus(javaEnumTypes)
453
544
.plus(javaDataFetchers)
454
545
.plus(javaQueryTypes)
455
546
.plus(clientProjections)
456
547
.plus(javaConstants)
457
- .toList()
458
548
}
459
549
460
550
fun kotlinSources (): List <FileSpec > {
461
551
return kotlinDataTypes
462
- .asSequence( )
552
+ .plus(kotlinInputTypes )
463
553
.plus(kotlinInterfaces)
464
554
.plus(kotlinEnumTypes)
465
555
.plus(kotlinConstants)
466
- .toList( )
556
+ .plus(kotlinClientTypes )
467
557
}
468
558
}
469
559
@@ -498,21 +588,10 @@ fun List<FieldDefinition>.filterIncludedInConfig(definitionName: String, config:
498
588
}
499
589
}
500
590
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 (
513
592
config : CodeGenConfig
514
593
): 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)
516
595
}
517
596
518
597
fun TypeDefinition <* >.fieldDefinitions (): List <FieldDefinition > {
0 commit comments