Skip to content

Commit

Permalink
WIP - adding a way to only generate types relevant for the generated …
Browse files Browse the repository at this point in the history
…client API
  • Loading branch information
paulbakker committed Feb 20, 2021
1 parent 03692e0 commit 2b914ba
Show file tree
Hide file tree
Showing 14 changed files with 982 additions and 182 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ import java.nio.file.Path
import java.nio.file.Paths

class CodeGen(private val config: CodeGenConfig) {
lateinit var document: Document
lateinit var requiredTypeCollector: RequiredTypeCollector

fun generate(): Any {
if (config.writeToFiles) {
config.outputDir.toFile().deleteRecursively()
Expand Down Expand Up @@ -75,23 +78,14 @@ class CodeGen(private val config: CodeGenConfig) {
}

private fun generateForSchema(schema: String): CodeGenResult {
val document: Document = Parser().parseDocument(schema)
document = Parser().parseDocument(schema)
requiredTypeCollector = RequiredTypeCollector(document, queries = config.includeQueries)
val definitions = document.definitions
val dataTypesResult = generateJavaDataType(definitions, document)
val dataTypesResult = generateJavaDataType(definitions)
val inputTypesResult = generateJavaInputType(definitions)

val interfacesResult = definitions.filterIsInstance<InterfaceTypeDefinition>()
.map { InterfaceGenerator(config).generate(it, document) }
.fold(CodeGenResult()) { t: CodeGenResult, u: CodeGenResult -> t.merge(u) }

val unionsResult = definitions.filterIsInstance<UnionTypeDefinition>()
.map { UnionTypeGenerator(config).generate(it) }
.fold(CodeGenResult()) { t: CodeGenResult, u: CodeGenResult -> t.merge(u) }

val enumsResult = definitions.filterIsInstance<EnumTypeDefinition>()
.map { EnumTypeGenerator(config).generate(it) }
.fold(CodeGenResult()) { t: CodeGenResult, u: CodeGenResult -> t.merge(u) }

val interfacesResult = generateJavaInterfaces(definitions)
val unionsResult = generateJavaUnions(definitions)
val enumsResult = generateJavaEnums(definitions)
val dataFetchersResult = generateJavaDataFetchers(definitions)
val client = generateJavaClientApi(definitions, document)
val entitiesClient = generateJavaClientEntitiesApi(definitions, document)
Expand All @@ -101,6 +95,35 @@ class CodeGen(private val config: CodeGenConfig) {
return dataTypesResult.merge(dataFetchersResult).merge(inputTypesResult).merge(unionsResult).merge(enumsResult).merge(interfacesResult).merge(client).merge(entitiesClient).merge(entitiesRepresentationsTypes).merge(constantsClass)
}

private fun generateJavaEnums(definitions: MutableList<Definition<Definition<*>>>): CodeGenResult {
if(!config.generateDataTypes) {
return CodeGenResult()
}
return definitions.filterIsInstance<EnumTypeDefinition>()
.map { EnumTypeGenerator(config).generate(it) }
.fold(CodeGenResult()) { t: CodeGenResult, u: CodeGenResult -> t.merge(u) }
}

private fun generateJavaUnions(definitions: MutableList<Definition<Definition<*>>>): CodeGenResult {
if(!config.generateDataTypes) {
return CodeGenResult()
}

return definitions.filterIsInstance<UnionTypeDefinition>()
.map { UnionTypeGenerator(config).generate(it) }
.fold(CodeGenResult()) { t: CodeGenResult, u: CodeGenResult -> t.merge(u) }
}

private fun generateJavaInterfaces(definitions: MutableList<Definition<Definition<*>>>): CodeGenResult {
if(!config.generateDataTypes) {
return CodeGenResult()
}

return definitions.filterIsInstance<InterfaceTypeDefinition>()
.map { InterfaceGenerator(config).generate(it, document) }
.fold(CodeGenResult()) { t: CodeGenResult, u: CodeGenResult -> t.merge(u) }
}

private fun generateJavaClientApi(definitions: MutableList<Definition<Definition<*>>>, document: Document): CodeGenResult {
return if (config.generateClientApi) {
definitions.filterIsInstance<ObjectTypeDefinition>()
Expand Down Expand Up @@ -135,7 +158,11 @@ class CodeGen(private val config: CodeGenConfig) {
.fold(CodeGenResult()) { t: CodeGenResult, u: CodeGenResult -> t.merge(u) }
}

private fun generateJavaDataType(definitions: List<Definition<Definition<*>>>, document: Document): CodeGenResult {
private fun generateJavaDataType(definitions: List<Definition<Definition<*>>>): CodeGenResult {
if(!config.generateDataTypes) {
return CodeGenResult()
}

return definitions.filterIsInstance<ObjectTypeDefinition>()
.filter { it !is ObjectTypeExtensionDefinition && it.name != "Query" && it.name != "Mutation" && it.name != "RelayPageInfo" }
.map {
Expand All @@ -144,8 +171,13 @@ class CodeGen(private val config: CodeGenConfig) {
}

private fun generateJavaInputType(definitions: List<Definition<Definition<*>>>): CodeGenResult {
return definitions.filterIsInstance<InputObjectTypeDefinition>()
.filter { it !is InputObjectTypeExtensionDefinition }


val inputTypes = definitions.filterIsInstance<InputObjectTypeDefinition>()
.filter { it !is InputObjectTypeExtensionDefinition }
.filter { config.generateDataTypes || requiredTypeCollector.requiredTypes.contains(it.name) }

return inputTypes
.map { d ->
InputTypeGenerator(config).generate(d, findInputExtensions(d.name, definitions))
}.fold(CodeGenResult()) { t: CodeGenResult, u: CodeGenResult -> t.merge(u) }
Expand All @@ -158,11 +190,11 @@ class CodeGen(private val config: CodeGenConfig) {
definitions.filterIsInstance<InputObjectTypeExtensionDefinition>().filter { name == it.name }

private fun generateKotlinForSchema(schema: String): KotlinCodeGenResult {
val document: Document = Parser().parseDocument(schema)
document = Parser().parseDocument(schema)
val definitions = document.definitions

val datatypesResult = generateKotlinDataTypes(definitions, document)
val inputTypes = generateKotlinInputTypes(definitions, document)
val datatypesResult = generateKotlinDataTypes(definitions)
val inputTypes = generateKotlinInputTypes(definitions)

val interfacesResult = definitions.filterIsInstance<InterfaceTypeDefinition>()
.map { KotlinInterfaceTypeGenerator(config).generate(it, document) }
Expand All @@ -178,29 +210,29 @@ class CodeGen(private val config: CodeGenConfig) {

val constantsClass = KotlinConstantsGenerator(config, document).generate()

val client = generateKotlinClientApi(definitions, document)
val entitiesClient = generateKotlinClientEntitiesApi(definitions, document)
val entitiesRepresentationsTypes = generateKotlinClientEntitiesRepresentations(definitions, document)
val client = generateKotlinClientApi(definitions)
val entitiesClient = generateKotlinClientEntitiesApi(definitions)
val entitiesRepresentationsTypes = generateKotlinClientEntitiesRepresentations(definitions)

return datatypesResult.merge(inputTypes).merge(interfacesResult).merge(unionResult).merge(enumsResult).merge(client).merge(entitiesClient).merge(entitiesRepresentationsTypes).merge(constantsClass)
}

private fun generateKotlinClientApi(definitions: List<Definition<Definition<*>>>, document: Document): KotlinCodeGenResult {
private fun generateKotlinClientApi(definitions: List<Definition<Definition<*>>>): KotlinCodeGenResult {
return if (config.generateClientApi) {
definitions.filterIsInstance<ObjectTypeDefinition>().filter { it.name == "Query" || it.name == "Mutation" }
.map { KotlinClientApiGenerator(config, document).generate(it) }
.fold(KotlinCodeGenResult()) { t: KotlinCodeGenResult, u: KotlinCodeGenResult -> t.merge(u) }
} else KotlinCodeGenResult()
}

private fun generateKotlinClientEntitiesApi(definitions: MutableList<Definition<Definition<*>>>, document: Document): KotlinCodeGenResult {
private fun generateKotlinClientEntitiesApi(definitions: MutableList<Definition<Definition<*>>>): KotlinCodeGenResult {
return if (config.generateClientApi) {
val federatedDefinitions = definitions.filterIsInstance<ObjectTypeDefinition>().filter { it.getDirective("key") != null }
KotlinClientApiGenerator(config, document).generateEntities(federatedDefinitions)
} else KotlinCodeGenResult()
}

private fun generateKotlinClientEntitiesRepresentations(definitions: MutableList<Definition<Definition<*>>>, document: Document): KotlinCodeGenResult {
private fun generateKotlinClientEntitiesRepresentations(definitions: MutableList<Definition<Definition<*>>>): KotlinCodeGenResult {
return if (config.generateClientApi) {
val generatedRepresentations = mutableMapOf<String, Any>()
return definitions.filterIsInstance<ObjectTypeDefinition>()
Expand All @@ -211,15 +243,15 @@ class CodeGen(private val config: CodeGenConfig) {
} else KotlinCodeGenResult()
}

private fun generateKotlinInputTypes(definitions: List<Definition<Definition<*>>>, document: Document): KotlinCodeGenResult {
private fun generateKotlinInputTypes(definitions: List<Definition<Definition<*>>>): KotlinCodeGenResult {
return definitions.filterIsInstance<InputObjectTypeDefinition>()
.map {
KotlinInputTypeGenerator(config, document).generate(it, findInputExtensions(it.name, definitions))
}
.fold(KotlinCodeGenResult()) { t: KotlinCodeGenResult, u: KotlinCodeGenResult -> t.merge(u) }
}

private fun generateKotlinDataTypes(definitions: List<Definition<Definition<*>>>, document: Document): KotlinCodeGenResult {
private fun generateKotlinDataTypes(definitions: List<Definition<Definition<*>>>): KotlinCodeGenResult {
return definitions.filterIsInstance<ObjectTypeDefinition>()
.filter { it !is ObjectTypeExtensionDefinition && it.name != "Query" && it.name != "Mutation" && it.name != "RelayPageInfo" }
.map {
Expand Down Expand Up @@ -250,8 +282,7 @@ data class CodeGenConfig(
val includeMutations: Set<String> = emptySet(),
val skipEntityQueries: Boolean = false,
val shortProjectionNames: Boolean = false,


val generateDataTypes: Boolean = true,
) {
val packageNameClient: String
get() = "${packageName}.${subPackageNameClient}"
Expand All @@ -261,6 +292,27 @@ data class CodeGenConfig(

val packageNameTypes: String
get() = "${packageName}.${subPackageNameTypes}"

override fun toString(): String {
return """
--output-dir=${outputDir}
--package-name=${packageName}
--sub-package-name-client=${subPackageNameClient}
--sub-package-name-datafetchers=${subPackageNameDatafetchers}
--sub-package-name-types=${subPackageNameTypes}
${if(generateBoxedTypes)"--generate-boxed-types" else ""}
${if(writeToFiles) "--write-to-disk" else ""}
--language=${language}
${if(generateClientApi) "--generate-client" else ""}
${if(generateDataTypes) "--generate-data-types" else "--skip-generate-data-types" }
${includeQueries.joinToString("\n") { "--include-query=${it}" }}
${includeMutations.joinToString("\n") { "--include-mutation=${it}" }}
${if(skipEntityQueries) "--skip-entities" else ""}
${typeMapping.map { "--type-mapping ${it.key}=${it.value}"}.joinToString("\n")}
${if(shortProjectionNames) "--short-projection-names" else ""}
${schemas.joinToString(" ")}
""".trimIndent()
}
}

enum class Language {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,26 @@ import java.nio.file.Paths
class CodeGenCli : CliktCommand("Generate Java sources for SCHEMA file(s)") {

private val schemas by argument().file(mustExist = true).multiple()
private val output by option("--output-dir", "-o", help = "Output directory").file(canBeFile = false, canBeDir = true).default(File("generated"))
private val output by option("--output-dir", "-o", help = "Output directory").file(
canBeFile = false,
canBeDir = true
).default(File("generated"))
private val packageName by option("--package-name", "-p", help = "Package name for generated types")
private val subPackageNameClient by option("--sub-package-name-client", help = "Sub package name for generated client").default("client")
private val subPackageNameDatafetchers by option("--sub-package-name-datafetchers", help = "Sub package name for generated datafetchers").default("datafetchers")
private val subPackageNameTypes by option("--sub-package-name-types", help = "Sub package name for generated types").default("types")
private val writeFiles by option("--write-to-disk", "-w", help = "Write files to disk").flag("--console-output", default = true)
private val language by option("--language", "-l", help = "Output language").choice("java", "kotlin").default("java")
private val generateBoxedTypes by option("--generate-boxed-types", "-b", help = "Genereate boxed types").flag(default = false)
private val writeFiles by option("--write-to-disk", "-w", help = "Write files to disk").flag(
"--console-output",
default = true
)
private val language by option("--language", "-l", help = "Output language").choice("java", "kotlin", ignoreCase = true)
.default("java")
private val generateClient by option("--generate-client", "-c", help = "Genereate client api").flag(default = false)
private val generateDataTypes by option(
"--generate-data-types",
help = "Generate data types. Not needed when only generating an API"
).flag("--skip-generate-data-types", default = true)
private val includeQueries by option("--include-query").multiple().unique()
private val includeMutations by option("--include-mutation").multiple().unique()
private val skipEntityQueries by option("--skip-entities").flag()
Expand All @@ -48,9 +59,9 @@ class CodeGenCli : CliktCommand("Generate Java sources for SCHEMA file(s)") {


override fun run() {
val inputSchemas = if(schemas.isEmpty()) {
val inputSchemas = if (schemas.isEmpty()) {
val defaultSchemaPath = Paths.get("src", "main", "resources", "schema")
if(defaultSchemaPath.toFile().exists()) {
if (defaultSchemaPath.toFile().exists()) {
echo("No schema files or directories specified, defaulting to src/main/resources/schema")
setOf(defaultSchemaPath.toFile())
} else {
Expand All @@ -62,9 +73,9 @@ class CodeGenCli : CliktCommand("Generate Java sources for SCHEMA file(s)") {

val generate = CodeGen(
if(packageName != null) {
CodeGenConfig(schemaFiles = inputSchemas, writeToFiles = writeFiles, outputDir = output.toPath(), packageName = packageName!!, subPackageNameClient = subPackageNameClient, subPackageNameDatafetchers = subPackageNameDatafetchers, subPackageNameTypes = subPackageNameTypes, language = Language.valueOf(language.toUpperCase()), generateBoxedTypes = generateBoxedTypes, generateClientApi = generateClient, includeQueries = includeQueries, includeMutations = includeMutations, skipEntityQueries = skipEntityQueries, typeMapping = typeMapping, shortProjectionNames = shortProjectionNames)
CodeGenConfig(schemaFiles = inputSchemas, writeToFiles = writeFiles, outputDir = output.toPath(), packageName = packageName!!, subPackageNameClient = subPackageNameClient, subPackageNameDatafetchers = subPackageNameDatafetchers, subPackageNameTypes = subPackageNameTypes, language = Language.valueOf(language.toUpperCase()), generateBoxedTypes = generateBoxedTypes, generateClientApi = generateClient, includeQueries = includeQueries, includeMutations = includeMutations, skipEntityQueries = skipEntityQueries, typeMapping = typeMapping, shortProjectionNames = shortProjectionNames, generateDataTypes = generateDataTypes)
} else {
CodeGenConfig(schemaFiles = inputSchemas, writeToFiles = writeFiles, outputDir = output.toPath(), subPackageNameClient = subPackageNameClient, subPackageNameDatafetchers = subPackageNameDatafetchers, subPackageNameTypes = subPackageNameTypes, language = Language.valueOf(language.toUpperCase()), generateBoxedTypes = generateBoxedTypes, generateClientApi = generateClient, includeQueries = includeQueries, includeMutations = includeMutations, skipEntityQueries = skipEntityQueries, typeMapping = typeMapping, shortProjectionNames = shortProjectionNames)
CodeGenConfig(schemaFiles = inputSchemas, writeToFiles = writeFiles, outputDir = output.toPath(), subPackageNameClient = subPackageNameClient, subPackageNameDatafetchers = subPackageNameDatafetchers, subPackageNameTypes = subPackageNameTypes, language = Language.valueOf(language.toUpperCase()), generateBoxedTypes = generateBoxedTypes, generateClientApi = generateClient, includeQueries = includeQueries, includeMutations = includeMutations, skipEntityQueries = skipEntityQueries, typeMapping = typeMapping, shortProjectionNames = shortProjectionNames, generateDataTypes = generateDataTypes)
}
).generate()

Expand All @@ -81,4 +92,4 @@ class CodeGenCli : CliktCommand("Generate Java sources for SCHEMA file(s)") {

fun main(args: Array<String>) {
CodeGenCli().main(args)
}
}
Loading

0 comments on commit 2b914ba

Please sign in to comment.