From e04ef89c2a316586e8123d0cee80f2eb7fdbc434 Mon Sep 17 00:00:00 2001 From: Pierre Ricadat Date: Sat, 11 Jul 2020 11:37:02 +0900 Subject: [PATCH] Handle case class reserved fields in codegen (#497) --- .../scala/caliban/tools/ClientWriter.scala | 9 +++-- .../scala/caliban/tools/SchemaWriter.scala | 14 ++++---- .../caliban/tools/ClientWriterSpec.scala | 31 +++++++++++++++- .../caliban/tools/SchemaWriterSpec.scala | 35 +++++++++++++++---- 4 files changed, 72 insertions(+), 17 deletions(-) diff --git a/tools/src/main/scala/caliban/tools/ClientWriter.scala b/tools/src/main/scala/caliban/tools/ClientWriter.scala index e0c2296a85..43af9be10d 100644 --- a/tools/src/main/scala/caliban/tools/ClientWriter.scala +++ b/tools/src/main/scala/caliban/tools/ClientWriter.scala @@ -180,7 +180,9 @@ object ClientWriter { """ def safeName(name: String): String = - if (reservedKeywords.contains(name)) s"`$name`" else name + if (reservedKeywords.contains(name)) s"`$name`" + else if (caseClassReservedFields.contains(name)) s"${name}_" + else name @tailrec def getTypeLetter(typesMap: Map[String, TypeDefinition], letter: String = "A"): String = @@ -289,7 +291,7 @@ object ClientWriter { } def writeArgumentFields(args: List[InputValueDefinition]): String = - s"${args.map(arg => s"${safeName(arg.name)}: ${writeType(arg.ofType)}${writeDefaultArgument(arg)}").mkString(", ")}" + s"${args.map(arg => s"${safeName(arg.name)} : ${writeType(arg.ofType)}${writeDefaultArgument(arg)}").mkString(", ")}" def writeDefaultArgument(arg: InputValueDefinition): String = arg.ofType match { @@ -380,4 +382,7 @@ object ClientWriter { "yield", "_" ) + + val caseClassReservedFields = + Set("wait", "notify", "toString", "notifyAll", "hashCode", "getClass", "finalize", "equals", "clone") } diff --git a/tools/src/main/scala/caliban/tools/SchemaWriter.scala b/tools/src/main/scala/caliban/tools/SchemaWriter.scala index 9e8a5f185d..8ab4ecee14 100644 --- a/tools/src/main/scala/caliban/tools/SchemaWriter.scala +++ b/tools/src/main/scala/caliban/tools/SchemaWriter.scala @@ -93,7 +93,7 @@ object SchemaWriter { def writeRootField(field: FieldDefinition, effect: String): String = { val argsName = if (field.args.nonEmpty) s" ${field.name.capitalize}Args =>" else "" - s"${safeName(field.name)}:$argsName $effect[${writeType(field.ofType)}]" + s"${safeName(field.name)} :$argsName $effect[${writeType(field.ofType)}]" } def writeRootQueryOrMutationDef(op: ObjectTypeDefinition, effect: String): String = @@ -122,7 +122,7 @@ object SchemaWriter { def writeInputObject(typedef: InputObjectTypeDefinition): String = s"""${writeDescription(typedef.description)}case class ${typedef.name}(${typedef.fields - .map(writeInputValue(_, typedef)) + .map(writeInputValue) .mkString(", ")})""" def writeEnum(typedef: EnumTypeDefinition): String = @@ -147,17 +147,17 @@ object SchemaWriter { def writeField(field: FieldDefinition, of: ObjectTypeDefinition): String = if (field.args.nonEmpty) { - s"${writeDescription(field.description)}${safeName(field.name)}: ${of.name.capitalize}${field.name.capitalize}Args => ${writeType(field.ofType)}" + s"${writeDescription(field.description)}${safeName(field.name)} : ${of.name.capitalize}${field.name.capitalize}Args => ${writeType(field.ofType)}" } else { - s"""${writeDescription(field.description)}${safeName(field.name)}: ${writeType(field.ofType)}""" + s"""${writeDescription(field.description)}${safeName(field.name)} : ${writeType(field.ofType)}""" } - def writeInputValue(value: InputValueDefinition, of: InputObjectTypeDefinition): String = - s"""${writeDescription(value.description)}${safeName(value.name)}: ${writeType(value.ofType)}""" + def writeInputValue(value: InputValueDefinition): String = + s"""${writeDescription(value.description)}${safeName(value.name)} : ${writeType(value.ofType)}""" def writeArguments(field: FieldDefinition): String = { def fields(args: List[InputValueDefinition]): String = - s"${args.map(arg => s"${safeName(arg.name)}: ${writeType(arg.ofType)}").mkString(", ")}" + s"${args.map(arg => s"${safeName(arg.name)} : ${writeType(arg.ofType)}").mkString(", ")}" if (field.args.nonEmpty) { s"case class ${field.name.capitalize}Args(${fields(field.args)})" diff --git a/tools/src/test/scala/caliban/tools/ClientWriterSpec.scala b/tools/src/test/scala/caliban/tools/ClientWriterSpec.scala index e2aa39e7cf..f18900871f 100644 --- a/tools/src/test/scala/caliban/tools/ClientWriterSpec.scala +++ b/tools/src/test/scala/caliban/tools/ClientWriterSpec.scala @@ -263,6 +263,35 @@ object Client { } } +} +""" + ) + ) + }, + testM("input object with reserved name") { + val schema = + """ + input CharacterInput { + wait: String! + } + """.stripMargin + + assertM(gen(schema))( + equalTo( + """import caliban.client._ +import caliban.client.Value._ + +object Client { + + case class CharacterInput(wait_ : String) + object CharacterInput { + implicit val encoder: ArgEncoder[CharacterInput] = new ArgEncoder[CharacterInput] { + override def encode(value: CharacterInput): Value = + ObjectValue(List("wait" -> implicitly[ArgEncoder[String]].encode(value.wait_))) + override def typeName: String = "CharacterInput" + } + } + } """ ) @@ -377,7 +406,7 @@ object Client { /** * name */ - @deprecated(${tripleQuotes}foo\nbar${tripleQuotes}, "") + @deprecated(${tripleQuotes}foo\nbar$tripleQuotes, "") def name: SelectionBuilder[Character, String] = Field("name", Scalar()) } diff --git a/tools/src/test/scala/caliban/tools/SchemaWriterSpec.scala b/tools/src/test/scala/caliban/tools/SchemaWriterSpec.scala index 6c518a271c..2bef453d2e 100644 --- a/tools/src/test/scala/caliban/tools/SchemaWriterSpec.scala +++ b/tools/src/test/scala/caliban/tools/SchemaWriterSpec.scala @@ -28,6 +28,7 @@ object SchemaWriterSpec extends DefaultRunnableSpec { val typeCaseClass = Parser .parseQuery(schema) .map(_.objectTypeDefinitions.map(SchemaWriter.writeObject).mkString("\n")) + .flatMap(Formatter.format(_, None).map(_.trim)) val typeCaseClassArgs = Parser .parseQuery(schema) @@ -38,6 +39,7 @@ object SchemaWriterSpec extends DefaultRunnableSpec { argClass = SchemaWriter.writeArguments(typeDefField) if argClass.length > 0 } yield argClass).mkString("\n") } + .flatMap(Formatter.format(_, None).map(_.trim)) assertM(typeCaseClass)( equalTo( @@ -67,13 +69,13 @@ object SchemaWriterSpec extends DefaultRunnableSpec { .map( _.objectTypeDefinition("Query").map(SchemaWriter.writeRootQueryOrMutationDef(_, "zio.UIO")).mkString("\n") ) + .flatMap(Formatter.format(_, None).map(_.trim)) assertM(result)( equalTo( - """ -case class Query( -user: UserArgs => zio.UIO[Option[User]], -userList: zio.UIO[List[Option[User]]] + """case class Query( + user: UserArgs => zio.UIO[Option[User]], + userList: zio.UIO[List[Option[User]]] )""".stripMargin ) ) @@ -92,12 +94,12 @@ userList: zio.UIO[List[Option[User]]] .map(SchemaWriter.writeRootQueryOrMutationDef(_, "zio.UIO")) .mkString("\n") ) + .flatMap(Formatter.format(_, None).map(_.trim)) assertM(result)( equalTo( - """ - |case class Mutation( - |setMessage: SetMessageArgs => zio.UIO[Option[String]] + """case class Mutation( + | setMessage: SetMessageArgs => zio.UIO[Option[String]] |)""".stripMargin ) ) @@ -338,6 +340,25 @@ object Types { case class Character(`private`: String, `object`: String, `type`: String) +} +""" + ) + ) + }, + testM("case class reserved field name used") { + val schema = + """ + type Character { + wait: String! + } + """.stripMargin + + assertM(gen(schema))( + equalTo( + """object Types { + + case class Character(wait_ : String) + } """ )