Skip to content

Commit edb449e

Browse files
authored
Merge pull request #416 from Netflix/feature/KotlinEntitiesClientApiGenTest.kt
2 parents 7a5bdcf + 2f225c8 commit edb449e

File tree

6 files changed

+223
-184
lines changed

6 files changed

+223
-184
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
*
3+
* Copyright 2020 Netflix, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
package com.netflix.graphql.dgs.codegen.generators
20+
21+
import com.netflix.graphql.dgs.codegen.CodeGenConfig
22+
import com.netflix.graphql.dgs.codegen.CodeGenResult
23+
import graphql.language.*
24+
25+
object EntitiesRepresentationTypeGeneratorUtils {
26+
27+
fun interface RepresentationGenerator {
28+
fun generate(
29+
definitionName: String,
30+
representationName: String,
31+
fields: List<FieldDefinition>,
32+
generatedRepresentations: MutableMap<String, Any>,
33+
keyFields: Map<String, Any>
34+
): CodeGenResult
35+
}
36+
37+
fun generate(
38+
config: CodeGenConfig,
39+
definition: ObjectTypeDefinition,
40+
generatedRepresentations: MutableMap<String, Any>,
41+
representationGenerator: RepresentationGenerator
42+
): CodeGenResult {
43+
if (config.skipEntityQueries) {
44+
return CodeGenResult()
45+
}
46+
val representationName = toRepresentationName(definition)
47+
if (representationName in generatedRepresentations) {
48+
return CodeGenResult()
49+
}
50+
51+
val directiveArg =
52+
definition
53+
.getDirectives("key")
54+
.map { it.argumentsByName["fields"]?.value as StringValue }
55+
.map { it.value }
56+
57+
val keyFields = parseKeyDirectiveValue(directiveArg)
58+
59+
return representationGenerator.generate(
60+
definition.name,
61+
representationName,
62+
definition.fieldDefinitions,
63+
generatedRepresentations,
64+
keyFields
65+
)
66+
}
67+
68+
fun findType(typeName: Type<*>, document: Document): TypeDefinition<*>? {
69+
return when (typeName) {
70+
is NonNullType -> {
71+
findType(typeName.type, document)
72+
}
73+
is ListType -> {
74+
findType(typeName.type, document)
75+
}
76+
else -> document.definitions.filterIsInstance<TypeDefinition<*>>()
77+
.find { it.name == (typeName as TypeName).name }
78+
}
79+
}
80+
81+
fun parseKeyDirectiveValue(keyDirective: List<String>): Map<String, Any> {
82+
data class Node(val key: String, val map: MutableMap<String, Any>, val parent: Node?)
83+
84+
val keys = keyDirective.map { ds ->
85+
ds.map { if (it == '{' || it == '}') " $it " else "$it" }
86+
.joinToString("", "", "")
87+
.split(" ")
88+
}.flatten()
89+
90+
// handle simple keys and nested keys by constructing the path to each key
91+
// e.g. type Movie @key(fields: "movieId") or type MovieCast @key(fields: movie { movieId } actors { name } }
92+
val mappedKeyTypes = mutableMapOf<String, Any>()
93+
var parent = Node("", mappedKeyTypes, null)
94+
var current = Node("", mappedKeyTypes, null)
95+
keys.filter { it != " " && it != "" }
96+
.forEach {
97+
when (it) {
98+
"{" -> {
99+
// push a new map for the next level
100+
val previous = parent
101+
parent = current
102+
current = Node("", current.map[current.key] as MutableMap<String, Any>, previous)
103+
}
104+
"}" -> {
105+
// pop back to parent level
106+
current = parent
107+
parent = parent.parent!!
108+
}
109+
else -> {
110+
// make an entry at the current level
111+
current.map.putIfAbsent(it, mutableMapOf<String, Any>())
112+
current = Node(it, current.map, parent)
113+
}
114+
}
115+
}
116+
return mappedKeyTypes
117+
}
118+
119+
fun toRepresentationName(definition: TypeDefinition<*>) = "${definition.name}Representation"
120+
}

graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/java/EntitiesRepresentationTypeGenerator.kt

Lines changed: 17 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,16 @@ package com.netflix.graphql.dgs.codegen.generators.java
2121
import com.netflix.graphql.dgs.codegen.CodeGenConfig
2222
import com.netflix.graphql.dgs.codegen.CodeGenResult
2323
import com.netflix.graphql.dgs.codegen.fieldDefinitions
24+
import com.netflix.graphql.dgs.codegen.generators.EntitiesRepresentationTypeGeneratorUtils
25+
import com.netflix.graphql.dgs.codegen.generators.EntitiesRepresentationTypeGeneratorUtils.findType
26+
import com.netflix.graphql.dgs.codegen.generators.EntitiesRepresentationTypeGeneratorUtils.toRepresentationName
2427
import com.squareup.javapoet.ClassName
2528
import com.squareup.javapoet.CodeBlock
26-
import graphql.language.*
29+
import graphql.language.Document
30+
import graphql.language.EnumTypeDefinition
31+
import graphql.language.FieldDefinition
32+
import graphql.language.InterfaceTypeDefinition
33+
import graphql.language.ObjectTypeDefinition
2734
import org.slf4j.LoggerFactory
2835

2936
@Suppress("UNCHECKED_CAST")
@@ -32,27 +39,15 @@ class EntitiesRepresentationTypeGenerator(
3239
document: Document
3340
) : BaseDataTypeGenerator(config.packageNameClient, config, document) {
3441

35-
fun generate(definition: ObjectTypeDefinition, generatedRepresentations: MutableMap<String, Any>): CodeGenResult {
36-
if (config.skipEntityQueries) {
37-
return CodeGenResult()
38-
}
39-
val representationName = toRepresentationName(definition)
40-
if (generatedRepresentations.containsKey(representationName)) {
41-
return CodeGenResult()
42-
}
43-
val directiveArg =
44-
definition
45-
.getDirectives("key")
46-
.map { it.argumentsByName["fields"]?.value as StringValue }
47-
.map { it.value }
48-
49-
val keyFields = parseKeyDirectiveValue(directiveArg)
50-
return generateRepresentations(
51-
definition.name,
52-
representationName,
53-
definition.fieldDefinitions,
42+
fun generate(
43+
definition: ObjectTypeDefinition,
44+
generatedRepresentations: MutableMap<String, Any>
45+
): CodeGenResult {
46+
return EntitiesRepresentationTypeGeneratorUtils.generate(
47+
config,
48+
definition,
5449
generatedRepresentations,
55-
keyFields
50+
this::generateRepresentations
5651
)
5752
}
5853

@@ -74,7 +69,6 @@ class EntitiesRepresentationTypeGenerator(
7469
.filter { keyFields.containsKey(it.name) }
7570
.map {
7671
val type = findType(it.type, document)
77-
7872
if (type != null &&
7973
(
8074
type is ObjectTypeDefinition ||
@@ -105,8 +99,7 @@ class EntitiesRepresentationTypeGenerator(
10599
}
106100
Field(it.name, ClassName.get("", fieldRepresentationType))
107101
} else {
108-
val returnType = typeUtils.findReturnType(it.type)
109-
Field(it.name, returnType)
102+
Field(it.name, typeUtils.findReturnType(it.type))
110103
}
111104
}
112105
// Generate base type representation...
@@ -121,61 +114,8 @@ class EntitiesRepresentationTypeGenerator(
121114
return parentRepresentationCodeGen.merge(fieldsCodeGenAccumulator)
122115
}
123116

124-
private fun findType(typeName: Type<*>, document: Document): TypeDefinition<*>? {
125-
return when (typeName) {
126-
is NonNullType -> {
127-
findType(typeName.type, document)
128-
}
129-
is ListType -> {
130-
findType(typeName.type, document)
131-
}
132-
else -> document.definitions.filterIsInstance<TypeDefinition<*>>()
133-
.find { it.name == (typeName as TypeName).name }
134-
}
135-
}
136-
137-
private fun parseKeyDirectiveValue(keyDirective: List<String>): Map<String, Any> {
138-
data class Node(val key: String, val map: MutableMap<String, Any>, val parent: Node?)
139-
140-
val keys = keyDirective.map { ds ->
141-
ds.map { if (it == '{' || it == '}') " $it " else "$it" }
142-
.joinToString("", "", "")
143-
.split(" ")
144-
}.flatten()
145-
146-
// handle simple keys and nested keys by constructing the path to each key
147-
// e.g. type Movie @key(fields: "movieId") or type MovieCast @key(fields: movie { movieId } actors { name } }
148-
val mappedKeyTypes = mutableMapOf<String, Any>()
149-
var parent = Node("", mappedKeyTypes, null)
150-
var current = Node("", mappedKeyTypes, null)
151-
keys.filter { it != " " && it != "" }
152-
.forEach {
153-
when (it) {
154-
"{" -> {
155-
// push a new map for the next level
156-
val previous = parent
157-
parent = current
158-
current = Node("", current.map[current.key] as MutableMap<String, Any>, previous)
159-
}
160-
"}" -> {
161-
// pop back to parent level
162-
current = parent
163-
parent = parent.parent!!
164-
}
165-
else -> {
166-
// make an entry at the current level
167-
current.map.putIfAbsent(it, mutableMapOf<String, Any>())
168-
current = Node(it, current.map, parent)
169-
}
170-
}
171-
}
172-
return mappedKeyTypes
173-
}
174-
175117
companion object {
176118
private val logger: org.slf4j.Logger =
177119
LoggerFactory.getLogger(EntitiesRepresentationTypeGenerator::class.java)
178-
179-
private fun toRepresentationName(definition: TypeDefinition<*>) = "${definition.name}Representation"
180120
}
181121
}

graphql-dgs-codegen-core/src/main/kotlin/com/netflix/graphql/dgs/codegen/generators/kotlin/KotlinDataTypeGenerator.kt

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,6 @@ class KotlinDataTypeGenerator(config: CodeGenConfig, document: Document) :
6868
val interfaces = definition.implements
6969
return generate(definition.name, fields, interfaces, document, definition.description)
7070
}
71-
72-
override fun getPackageName(): String {
73-
return config.packageNameTypes
74-
}
7571
}
7672

7773
class KotlinInputTypeGenerator(config: CodeGenConfig, document: Document) :
@@ -119,10 +115,6 @@ class KotlinInputTypeGenerator(config: CodeGenConfig, document: Document) :
119115
is ParameterizedTypeName -> typeArguments[0].className
120116
else -> TODO()
121117
}
122-
123-
override fun getPackageName(): String {
124-
return config.packageNameTypes
125-
}
126118
}
127119

128120
internal data class Field(
@@ -245,5 +237,7 @@ abstract class AbstractKotlinDataTypeGenerator(
245237
return CodeGenResult(kotlinDataTypes = listOf(fileSpec))
246238
}
247239

248-
abstract fun getPackageName(): String
240+
open fun getPackageName(): String {
241+
return config.packageNameTypes
242+
}
249243
}

0 commit comments

Comments
 (0)