From c28285de28daad088b47359bcdc67ae6da1db1e6 Mon Sep 17 00:00:00 2001 From: LaurenceWarne <17688577+LaurenceWarne@users.noreply.github.com> Date: Thu, 2 Dec 2021 11:32:41 +0000 Subject: [PATCH] Schema Gen: Add Option for Preserving Input Names (#1186) --- .../scala/caliban/codegen/CalibanCli.scala | 4 +- .../caliban/tools/CalibanCommonSettings.scala | 14 +++++-- .../main/scala/caliban/tools/Codegen.scala | 4 +- .../main/scala/caliban/tools/Options.scala | 9 ++-- .../scala/caliban/tools/SchemaWriter.scala | 10 +++-- .../caliban/tools/compiletime/Config.scala | 3 +- .../scala/caliban/tools/CodegenSpec.scala | 3 +- .../scala/caliban/tools/OptionsSpec.scala | 42 ++++++++++++++++++- .../caliban/tools/SchemaWriterSpec.scala | 30 ++++++++++++- .../tools/compiletime/ConfigSpec.scala | 3 +- vuepress/docs/docs/schema.md | 2 + 11 files changed, 105 insertions(+), 19 deletions(-) diff --git a/codegen-sbt/src/main/scala/caliban/codegen/CalibanCli.scala b/codegen-sbt/src/main/scala/caliban/codegen/CalibanCli.scala index a8671b8fd..7685c955f 100644 --- a/codegen-sbt/src/main/scala/caliban/codegen/CalibanCli.scala +++ b/codegen-sbt/src/main/scala/caliban/codegen/CalibanCli.scala @@ -52,7 +52,7 @@ object CalibanCli { private val genSchemaHelpMsg = s""" - |calibanGenSchema schemaPath outputPath [--scalafmtPath path] [--headers name:value,name2:value2] [--packageName name] [--effect fqdn.Effect] [--abstractEffectType true|false] + |calibanGenSchema schemaPath outputPath [--scalafmtPath path] [--headers name:value,name2:value2] [--packageName name] [--effect fqdn.Effect] [--abstractEffectType true|false] [--preserveInputNames true|false] | |This command will create a Scala file in `outputPath` containing all the types |defined in the provided GraphQL schema defined at `schemaPath`. Instead of a path, @@ -66,6 +66,8 @@ object CalibanCli { |type is abstract, so that it will be added as a type parameter to the generated |Query and Mutation classes (if applicable). In such cases `F` will be used by |as the type parameter unless the `--effect` option is explicitly given. + |By default all input types will have 'Input' appended to their names in the + |derived schema. Use the --preserveInputNames flag to disable this. |""".stripMargin private val genClientHelpMsg = diff --git a/tools/src/main/scala/caliban/tools/CalibanCommonSettings.scala b/tools/src/main/scala/caliban/tools/CalibanCommonSettings.scala index 1ee105a0e..aa914643b 100644 --- a/tools/src/main/scala/caliban/tools/CalibanCommonSettings.scala +++ b/tools/src/main/scala/caliban/tools/CalibanCommonSettings.scala @@ -15,7 +15,8 @@ final case class CalibanCommonSettings( extensibleEnums: Option[Boolean], genType: GenType, effect: Option[String], - abstractEffectType: Option[Boolean] + abstractEffectType: Option[Boolean], + preserveInputNames: Option[Boolean] ) { private[caliban] def toOptions(schemaPath: String, toPath: String): Options = @@ -33,7 +34,8 @@ final case class CalibanCommonSettings( abstractEffectType = abstractEffectType, splitFiles = splitFiles, enableFmt = enableFmt, - extensibleEnums = extensibleEnums + extensibleEnums = extensibleEnums, + preserveInputNames = preserveInputNames ) private[caliban] def combine(r: => CalibanCommonSettings): CalibanCommonSettings = @@ -50,7 +52,8 @@ final case class CalibanCommonSettings( extensibleEnums = r.extensibleEnums.orElse(this.extensibleEnums), genType = r.genType, effect = r.effect.orElse(this.effect), - abstractEffectType = r.abstractEffectType.orElse(this.abstractEffectType) + abstractEffectType = r.abstractEffectType.orElse(this.abstractEffectType), + preserveInputNames = r.preserveInputNames.orElse(this.preserveInputNames) ) def clientName(value: String): CalibanCommonSettings = this.copy(clientName = Some(value)) @@ -68,6 +71,8 @@ final case class CalibanCommonSettings( def effect(effect: String): CalibanCommonSettings = this.copy(effect = Some(effect)) def abstractEffectType(abstractEffectType: Boolean): CalibanCommonSettings = this.copy(abstractEffectType = Some(abstractEffectType)) + def preserveInputNames(preserveInputNames: Boolean): CalibanCommonSettings = + this.copy(preserveInputNames = Some(preserveInputNames)) } object CalibanCommonSettings { @@ -85,6 +90,7 @@ object CalibanCommonSettings { extensibleEnums = None, genType = GenType.Client, effect = None, - abstractEffectType = None + abstractEffectType = None, + preserveInputNames = None ) } diff --git a/tools/src/main/scala/caliban/tools/Codegen.scala b/tools/src/main/scala/caliban/tools/Codegen.scala index b425e50c3..3fb7d7c91 100644 --- a/tools/src/main/scala/caliban/tools/Codegen.scala +++ b/tools/src/main/scala/caliban/tools/Codegen.scala @@ -27,6 +27,7 @@ object Codegen { effect = arguments.effect.getOrElse { if (abstractEffectType) "F" else "zio.UIO" } + preserveInputNames = arguments.preserveInputNames.getOrElse(false) genView = arguments.genView.getOrElse(false) scalarMappings = arguments.scalarMappings splitFiles = arguments.splitFiles.getOrElse(false) @@ -41,7 +42,8 @@ object Codegen { effect, arguments.imports, scalarMappings, - abstractEffectType + abstractEffectType, + preserveInputNames ) ) case GenType.Client => diff --git a/tools/src/main/scala/caliban/tools/Options.scala b/tools/src/main/scala/caliban/tools/Options.scala index da75df6bd..aee490ea8 100644 --- a/tools/src/main/scala/caliban/tools/Options.scala +++ b/tools/src/main/scala/caliban/tools/Options.scala @@ -17,7 +17,8 @@ final case class Options( abstractEffectType: Option[Boolean], splitFiles: Option[Boolean], enableFmt: Option[Boolean], - extensibleEnums: Option[Boolean] + extensibleEnums: Option[Boolean], + preserveInputNames: Option[Boolean] ) object Options { @@ -34,7 +35,8 @@ object Options { abstractEffectType: Option[Boolean], splitFiles: Option[Boolean], enableFmt: Option[Boolean], - extensibleEnums: Option[Boolean] + extensibleEnums: Option[Boolean], + preserveInputNames: Option[Boolean] ) def fromArgs(args: List[String]): Option[Options] = @@ -77,7 +79,8 @@ object Options { rawOpts.abstractEffectType, rawOpts.splitFiles, rawOpts.enableFmt, - rawOpts.extensibleEnums + rawOpts.extensibleEnums, + rawOpts.preserveInputNames ) } case _ => None diff --git a/tools/src/main/scala/caliban/tools/SchemaWriter.scala b/tools/src/main/scala/caliban/tools/SchemaWriter.scala index 0a7177d48..440e098cc 100644 --- a/tools/src/main/scala/caliban/tools/SchemaWriter.scala +++ b/tools/src/main/scala/caliban/tools/SchemaWriter.scala @@ -12,7 +12,8 @@ object SchemaWriter { effect: String = "zio.UIO", imports: Option[List[String]] = None, scalarMappings: Option[Map[String, String]], - isEffectTypeAbstract: Boolean = false + isEffectTypeAbstract: Boolean = false, + preserveInputNames: Boolean = false ): String = { val interfaceImplementationsMap = (for { @@ -62,10 +63,13 @@ object SchemaWriter { .map(writeField(_, typedef)) .mkString(", ")})""" - def writeInputObject(typedef: InputObjectTypeDefinition): String = - s"""${writeDescription(typedef.description)}final case class ${typedef.name}(${typedef.fields + def writeInputObject(typedef: InputObjectTypeDefinition): String = { + val name = typedef.name + val maybeAnnotation = if (preserveInputNames) s"""@GQLInputName("$name")\n""" else "" + s"""${maybeAnnotation}${writeDescription(typedef.description)}final case class $name(${typedef.fields .map(writeInputValue) .mkString(", ")})""" + } def writeEnum(typedef: EnumTypeDefinition): String = s"""${writeDescription(typedef.description)}sealed trait ${typedef.name} extends scala.Product with scala.Serializable diff --git a/tools/src/main/scala/caliban/tools/compiletime/Config.scala b/tools/src/main/scala/caliban/tools/compiletime/Config.scala index 7437848ea..785a6843e 100644 --- a/tools/src/main/scala/caliban/tools/compiletime/Config.scala +++ b/tools/src/main/scala/caliban/tools/compiletime/Config.scala @@ -29,7 +29,8 @@ trait Config { extensibleEnums = Some(extensibleEnums), GenType.Client, effect = None, - abstractEffectType = None + abstractEffectType = None, + preserveInputNames = None ) private[caliban] def asScalaCode: String = { diff --git a/tools/src/test/scala/caliban/tools/CodegenSpec.scala b/tools/src/test/scala/caliban/tools/CodegenSpec.scala index 65a20a790..1b02746cb 100644 --- a/tools/src/test/scala/caliban/tools/CodegenSpec.scala +++ b/tools/src/test/scala/caliban/tools/CodegenSpec.scala @@ -81,7 +81,8 @@ object CodegenSpec extends DefaultRunnableSpec { abstractEffectType = None, splitFiles = None, enableFmt = None, - extensibleEnums = None + extensibleEnums = None, + preserveInputNames = None ) getPackageAndObjectName(arguments) diff --git a/tools/src/test/scala/caliban/tools/OptionsSpec.scala b/tools/src/test/scala/caliban/tools/OptionsSpec.scala index c52494c0a..11be3a2ff 100644 --- a/tools/src/test/scala/caliban/tools/OptionsSpec.scala +++ b/tools/src/test/scala/caliban/tools/OptionsSpec.scala @@ -28,6 +28,7 @@ object OptionsSpec extends DefaultRunnableSpec { None, None, None, + None, None ) ) @@ -54,6 +55,7 @@ object OptionsSpec extends DefaultRunnableSpec { None, None, None, + None, None ) ) @@ -66,7 +68,7 @@ object OptionsSpec extends DefaultRunnableSpec { assert(result)( equalTo( Some( - Options("schema", "output", None, None, None, None, None, None, None, None, None, None, None, None) + Options("schema", "output", None, None, None, None, None, None, None, None, None, None, None, None, None) ) ) ) @@ -105,6 +107,7 @@ object OptionsSpec extends DefaultRunnableSpec { None, None, None, + None, None ) ) @@ -131,6 +134,7 @@ object OptionsSpec extends DefaultRunnableSpec { None, None, None, + None, None ) ) @@ -157,6 +161,7 @@ object OptionsSpec extends DefaultRunnableSpec { None, None, None, + None, None ) ) @@ -183,6 +188,7 @@ object OptionsSpec extends DefaultRunnableSpec { None, None, None, + None, None ) ) @@ -209,7 +215,8 @@ object OptionsSpec extends DefaultRunnableSpec { None, None, None, - Some(true) + Some(true), + None ) ) ) @@ -235,6 +242,7 @@ object OptionsSpec extends DefaultRunnableSpec { None, None, None, + None, None ) ) @@ -261,6 +269,7 @@ object OptionsSpec extends DefaultRunnableSpec { None, None, None, + None, None ) ) @@ -287,12 +296,40 @@ object OptionsSpec extends DefaultRunnableSpec { Some(true), None, None, + None, None ) ) ) ) }, + test("provide preserveInputNames") { + val input = List("schema", "output", "--preserveInputNames", "true") + val result = Options.fromArgs(input) + assert(result)( + equalTo( + Some( + Options( + "schema", + "output", + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + Some(true) + ) + ) + ) + ) + }, test("header with a colon in the value") { val input = List("schema", "output", "--scalafmtPath", "fmtPath", "--headers", "aaa:bbb:ccc") val result = Options.fromArgs(input) @@ -313,6 +350,7 @@ object OptionsSpec extends DefaultRunnableSpec { None, None, None, + None, None ) ) diff --git a/tools/src/test/scala/caliban/tools/SchemaWriterSpec.scala b/tools/src/test/scala/caliban/tools/SchemaWriterSpec.scala index 9e22bdd08..d039055bd 100644 --- a/tools/src/test/scala/caliban/tools/SchemaWriterSpec.scala +++ b/tools/src/test/scala/caliban/tools/SchemaWriterSpec.scala @@ -15,7 +15,8 @@ object SchemaWriterSpec extends DefaultRunnableSpec { effect: String = "zio.UIO", imports: List[String] = List.empty, scalarMappings: Map[String, String] = Map.empty, - isEffectTypeAbstract: Boolean = false + isEffectTypeAbstract: Boolean = false, + preserveInputNames: Boolean = false ): RIO[Blocking, String] = Parser .parseQuery(schema.stripMargin) .flatMap(doc => @@ -27,7 +28,8 @@ object SchemaWriterSpec extends DefaultRunnableSpec { effect, Some(imports), Some(scalarMappings), - isEffectTypeAbstract + isEffectTypeAbstract, + preserveInputNames ), None ) @@ -353,6 +355,30 @@ object SchemaWriterSpec extends DefaultRunnableSpec { | |}""" ), + ( + "input type with preserved input", + gen( + """ + type Character { + name: String! + } + + input CharacterInput { + name: String! + } + """, + preserveInputNames = true + ), + """import caliban.schema.Annotations._ + | + |object Types { + | + | final case class Character(name: String) + | @GQLInputName("CharacterInput") + | final case class CharacterInput(name: String) + | + |}""" + ), ( "scala reserved word used", gen(""" diff --git a/tools/src/test/scala/caliban/tools/compiletime/ConfigSpec.scala b/tools/src/test/scala/caliban/tools/compiletime/ConfigSpec.scala index 5ffe636bf..8f823a634 100644 --- a/tools/src/test/scala/caliban/tools/compiletime/ConfigSpec.scala +++ b/tools/src/test/scala/caliban/tools/compiletime/ConfigSpec.scala @@ -39,7 +39,8 @@ object ConfigSpec extends DefaultRunnableSpec { enableFmt = Some(false), extensibleEnums = Some(true), effect = None, - abstractEffectType = None + abstractEffectType = None, + preserveInputNames = None ) ) ) diff --git a/vuepress/docs/docs/schema.md b/vuepress/docs/docs/schema.md index bca3bb0c3..721e15945 100644 --- a/vuepress/docs/docs/schema.md +++ b/vuepress/docs/docs/schema.md @@ -299,6 +299,8 @@ By default, each Query and Mutation will be wrapped into a `zio.UIO` effect. Thi You can also indicate that the effect type is abstract via `--abstractEffectType true`, in which case `Query` will be replaced by `Query[F[_]]` and so on (note `F` will be used unless `--effect ` is explicitly given in which case `` would be used in place of `F`). +By default the suffix `Input` is appended to the type name of input types in the derived schema. Use the `--preserveInputNames` flag to disable this. + If you want to force a mapping between a GraphQL type and a Scala class (such as scalars), you can use the `--scalarMappings` option. Also you can add additional imports by providing `--imports` option.