Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ class CodeGen(
* the schema information for the parser.
*/
private fun loadSchemaReaders(vararg readerBuilders: MultiSourceReader.Builder) {
validateNoOverlappingPaths(config.schemaFiles)

readerBuilders.forEach { rb ->
val schemaFiles =
config.schemaFiles
Expand All @@ -209,6 +211,28 @@ class CodeGen(
}
}

/**
* Validates that there are no overlapping paths in the schema files to prevent duplicate loading.
* Throws an [IllegalArgumentException] if any path is an ancestor of another.
*/
private fun validateNoOverlappingPaths(schemaFiles: Set<File>) {
val canonicalPaths = schemaFiles.map { it.canonicalFile }

for (i in canonicalPaths.indices) {
for (j in i + 1 until canonicalPaths.size) {
val path1 = canonicalPaths[i]
val path2 = canonicalPaths[j]

if (path1.startsWith(path2) || path2.startsWith(path1)) {
val (parent, child) = if (path1.startsWith(path2)) path2 to path1 else path1 to path2
logger.warn(
"Schema path '$child' is a descendant of '$parent'. Remove one of these paths from your configuration to avoid loading duplicate schema files.",
)
}
}
}
}

private fun generateJava(): CodeGenResult {
val definitions = document.definitions
// data types
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -681,9 +681,11 @@ class ClientApiGenerator(
): CodeGenResult =
if (type is InterfaceTypeDefinition) {
val concreteTypes =
document.getDefinitionsOfType(ObjectTypeDefinition::class.java).filter {
it.implements.filterIsInstance<NamedNode<*>>().any { iface -> iface.name == type.name }
}
document
.getDefinitionsOfType(ObjectTypeDefinition::class.java)
.filter {
it.implements.filterIsInstance<NamedNode<*>>().any { iface -> iface.name == type.name }
}.distinctBy { it.name }
concreteTypes
.map {
addFragmentProjectionMethod(javaType, root, it, processedEdges, queryDepth)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class DataTypeGenerator(
.map { it as TypeName }
.any { it.name == name }
}.map { it.name }
.distinct()
.toList()

var implements =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,7 @@ class EntitiesRepresentationTypeGenerator(
)
) {
val fieldTypeRepresentationName = toRepresentationName(type)
val fieldRepresentationType =
typeUtils
.findReturnType(it.type)
.toString()
.replace(type.name, fieldTypeRepresentationName)
val fieldRepresentationType = typeUtils.qualifyName(fieldTypeRepresentationName)

if (generatedRepresentations.containsKey(fieldTypeRepresentationName)) {
logger.trace("Representation for {} was already generated.", fieldTypeRepresentationName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,12 +266,14 @@ abstract class AbstractKotlinDataTypeGenerator(
}

val unionTypes =
document.getDefinitionsOfType(UnionTypeDefinition::class.java).filter { union ->
union.memberTypes
.asSequence()
.map { it as TypeName }
.any { it.name == name }
}
document
.getDefinitionsOfType(UnionTypeDefinition::class.java)
.filter { union ->
union.memberTypes
.asSequence()
.map { it as TypeName }
.any { it.name == name }
}.distinctBy { it.name }

val interfaceTypes = interfaces + unionTypes
interfaceTypes.forEach {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,7 @@ class KotlinEntitiesRepresentationTypeGenerator(
) {
val fieldType = typeUtils.findReturnType(it.type)
val fieldTypeRepresentationName = toRepresentationName(type)
val fieldRepresentationType =
fieldType
.toString()
.replace(type.name, fieldTypeRepresentationName)
.removeSuffix("?")
val fieldRepresentationType = typeUtils.qualifyName(fieldTypeRepresentationName)

if (generatedRepresentations.containsKey(fieldTypeRepresentationName)) {
logger.trace("Representation for {} was already generated.", fieldTypeRepresentationName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,46 @@ class EntitiesClientApiGenTest {
}
}

@Test
fun `Entity representation types should use client package not type-mapped package`() {
val schema =
"""
type Channel @key(fields: "id type") {
id: ID! @external
type: ChatType @external
name: String
}

enum ChatType {
PUBLIC
PRIVATE
DM
}
""".trimIndent()

val codeGenResult =
CodeGen(
CodeGenConfig(
schemas = setOf(schema),
packageName = BASE_PACKAGE_NAME,
generateClientApi = true,
generateDataTypes = false,
typeMapping = mapOf("ChatType" to "com.external.types.ChatType"),
),
).generate()

val representations = codeGenResult.javaDataTypes.filter { "Representation" in it.typeSpec.name }
assertThat(representations).hasSize(2)

val channelRepresentation = representations.find { it.typeSpec.name == "ChannelRepresentation" }
assertThat(channelRepresentation).isNotNull

val typeField = channelRepresentation!!.typeSpec.fieldSpecs.find { it.name == "type" }
assertThat(typeField).isNotNull
assertThat(typeField!!.type.toString())
.isEqualTo("$BASE_PACKAGE_NAME.client.ChatTypeRepresentation")
}

companion object {
fun codeGen(schema: String): CodeGenResult =
CodeGen(
Expand Down
Loading