diff --git a/codegen-sbt/src/main/scala/caliban/codegen/CalibanSettings.scala b/codegen-sbt/src/main/scala/caliban/codegen/CalibanSettings.scala index 25c30c036..be20b34b4 100644 --- a/codegen-sbt/src/main/scala/caliban/codegen/CalibanSettings.scala +++ b/codegen-sbt/src/main/scala/caliban/codegen/CalibanSettings.scala @@ -19,6 +19,8 @@ final case class CalibanFileSettings(file: File, settings: CalibanCommonSettings def splitFiles(value: Boolean): CalibanFileSettings = this.copy(settings = this.settings.splitFiles(value)) def enableFmt(value: Boolean): CalibanFileSettings = this.copy(settings = this.settings.enableFmt(value)) def extensibleEnums(value: Boolean): CalibanFileSettings = this.copy(settings = this.settings.extensibleEnums(value)) + def excludeClientDeprecation(value: Boolean): CalibanFileSettings = + this.copy(settings = this.settings.excludeClientDeprecation(value)) } final case class CalibanUrlSettings(url: URL, settings: CalibanCommonSettings) extends CalibanSettings { @@ -34,4 +36,6 @@ final case class CalibanUrlSettings(url: URL, settings: CalibanCommonSettings) e def splitFiles(value: Boolean): CalibanUrlSettings = this.copy(settings = this.settings.splitFiles(value)) def enableFmt(value: Boolean): CalibanUrlSettings = this.copy(settings = this.settings.enableFmt(value)) def extensibleEnums(value: Boolean): CalibanUrlSettings = this.copy(settings = this.settings.extensibleEnums(value)) + def excludeClientDepercation(value: Boolean): CalibanUrlSettings = + this.copy(settings = this.settings.excludeClientDeprecation(value)) } diff --git a/tools/src/main/scala/caliban/tools/CalibanCommonSettings.scala b/tools/src/main/scala/caliban/tools/CalibanCommonSettings.scala index b1b30d961..c411b1640 100644 --- a/tools/src/main/scala/caliban/tools/CalibanCommonSettings.scala +++ b/tools/src/main/scala/caliban/tools/CalibanCommonSettings.scala @@ -10,7 +10,8 @@ final case class CalibanCommonSettings( imports: Seq[String], splitFiles: Option[Boolean], enableFmt: Option[Boolean], - extensibleEnums: Option[Boolean] + extensibleEnums: Option[Boolean], + excludeClientDeprecation: Option[Boolean] ) { private[caliban] def toOptions(schemaPath: String, toPath: String): Options = @@ -28,7 +29,8 @@ final case class CalibanCommonSettings( abstractEffectType = Option.empty, splitFiles = splitFiles, enableFmt = enableFmt, - extensibleEnums = extensibleEnums + extensibleEnums = extensibleEnums, + excludeClientDeprecation = excludeClientDeprecation ) private[caliban] def combine(r: => CalibanCommonSettings): CalibanCommonSettings = @@ -42,7 +44,8 @@ final case class CalibanCommonSettings( imports = this.imports ++ r.imports, splitFiles = r.splitFiles.orElse(this.splitFiles), enableFmt = r.enableFmt.orElse(this.enableFmt), - extensibleEnums = r.extensibleEnums.orElse(this.extensibleEnums) + extensibleEnums = r.extensibleEnums.orElse(this.extensibleEnums), + excludeClientDeprecation = r.excludeClientDeprecation.orElse(this.excludeClientDeprecation) ) def clientName(value: String): CalibanCommonSettings = this.copy(clientName = Some(value)) @@ -56,6 +59,8 @@ final case class CalibanCommonSettings( def splitFiles(value: Boolean): CalibanCommonSettings = this.copy(splitFiles = Some(value)) def enableFmt(value: Boolean): CalibanCommonSettings = this.copy(enableFmt = Some(value)) def extensibleEnums(value: Boolean): CalibanCommonSettings = this.copy(extensibleEnums = Some(value)) + def excludeClientDeprecation(value: Boolean): CalibanCommonSettings = + this.copy(excludeClientDeprecation = Some(value)) } object CalibanCommonSettings { @@ -70,6 +75,7 @@ object CalibanCommonSettings { imports = Seq.empty, splitFiles = None, enableFmt = None, - extensibleEnums = None + extensibleEnums = None, + excludeClientDeprecation = None ) } diff --git a/tools/src/main/scala/caliban/tools/ClientWriter.scala b/tools/src/main/scala/caliban/tools/ClientWriter.scala index 5dd88196f..d3ceba069 100644 --- a/tools/src/main/scala/caliban/tools/ClientWriter.scala +++ b/tools/src/main/scala/caliban/tools/ClientWriter.scala @@ -21,12 +21,15 @@ object ClientWriter { genView: Boolean = false, additionalImports: Option[List[String]] = None, splitFiles: Boolean = false, - extensibleEnums: Boolean = false + extensibleEnums: Boolean = false, + excludeClientDeprecation: Boolean = false )(implicit scalarMappings: ScalarMappings): List[(String, String)] = { require(packageName.isDefined || !splitFiles, "splitFiles option requires a package name") val schemaDef = schema.schemaDefinition + implicit val excludeDeprecation: Boolean = excludeClientDeprecation + implicit val mappingClashedTypeNames: MappingClashedTypeNames = MappingClashedTypeNames( getMappingsClashedNames( schema.definitions.collect { @@ -295,7 +298,8 @@ object ClientWriter { )(implicit typesMap: TypesMap, mappingClashedTypeNames: MappingClashedTypeNames, - scalarMappings: ScalarMappings + scalarMappings: ScalarMappings, + excludeDeprecation: Boolean ): String = s"""object ${typedef.name} { | ${typedef.fields.map(writeField(_, "_root_.caliban.client.Operations.RootQuery")).mkString("\n ")} @@ -312,7 +316,8 @@ object ClientWriter { )(implicit typesMap: TypesMap, mappingClashedTypeNames: MappingClashedTypeNames, - scalarMappings: ScalarMappings + scalarMappings: ScalarMappings, + excludeDeprecation: Boolean ): String = s"""object ${typedef.name} { | ${typedef.fields.map(writeField(_, "_root_.caliban.client.Operations.RootMutation")).mkString("\n ")} @@ -329,7 +334,8 @@ object ClientWriter { )(implicit typesMap: TypesMap, mappingClashedTypeNames: MappingClashedTypeNames, - scalarMappings: ScalarMappings + scalarMappings: ScalarMappings, + excludeDeprecation: Boolean ): String = s"""object ${typedef.name} { | ${typedef.fields.map(writeField(_, "_root_.caliban.client.Operations.RootSubscription")).mkString("\n ")} @@ -354,7 +360,8 @@ object ClientWriter { )(implicit typesMap: TypesMap, mappingClashedTypeNames: MappingClashedTypeNames, - scalarMappings: ScalarMappings + scalarMappings: ScalarMappings, + excludeDeprecation: Boolean ): String = { val objectName: String = safeTypeName(typedef.name) @@ -617,7 +624,8 @@ object ClientWriter { )(implicit typesMap: TypesMap, mappingClashedTypeNames: MappingClashedTypeNames, - scalarMappings: ScalarMappings + scalarMappings: ScalarMappings, + excludeDeprecation: Boolean ): String = writeFieldInfo(collectFieldInfo(field, typeName)) @@ -648,27 +656,34 @@ object ClientWriter { )(implicit typesMap: TypesMap, mappingClashedTypeNames: MappingClashedTypeNames, - scalarMappings: ScalarMappings + scalarMappings: ScalarMappings, + excludeDeprecation: Boolean ): FieldInfo = { - val name = safeName(field.name) - val description = field.description match { + val name = safeName(field.name) + val description = field.description match { case Some(d) if d.trim.nonEmpty => s"/**\n * ${d.trim}\n */\n" case _ => "" } - val deprecated = field.directives.find(_.name == "deprecated") match { - case None => "" - case Some(directive) => - val body = - directive.arguments.collectFirst { case ("reason", StringValue(reason)) => - reason - }.getOrElse("") - - val quotes = - if (body.contains("\n")) tripleQuotes - else doubleQuotes - - "@deprecated(" + quotes + body + quotes + """, "")""" + "\n" + + val deprecated = excludeDeprecation match { + case true => "" + case false => + field.directives.find(_.name == "deprecated") match { + case None => "" + case Some(directive) => + val body = + directive.arguments.collectFirst { case ("reason", StringValue(reason)) => + reason + }.getOrElse("") + + val quotes = + if (body.contains("\n")) tripleQuotes + else doubleQuotes + + "@deprecated(" + quotes + body + quotes + """, "")""" + "\n" + } } + val fieldType = safeTypeName(getTypeName(field.ofType)) val isScalar = typesMap .get(fieldType) diff --git a/tools/src/main/scala/caliban/tools/Options.scala b/tools/src/main/scala/caliban/tools/Options.scala index da75df6bd..0b72f9ddf 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], + excludeClientDeprecation: 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], + excludeClientDeprecation: 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.excludeClientDeprecation ) } case _ => None diff --git a/tools/src/main/scala/caliban/tools/compiletime/Config.scala b/tools/src/main/scala/caliban/tools/compiletime/Config.scala index dd4ecc732..fa30bae6f 100644 --- a/tools/src/main/scala/caliban/tools/compiletime/Config.scala +++ b/tools/src/main/scala/caliban/tools/compiletime/Config.scala @@ -12,7 +12,8 @@ trait Config { imports: List[String] = List.empty, splitFiles: Boolean = false, enableFmt: Boolean = true, - extensibleEnums: Boolean = false + extensibleEnums: Boolean = false, + excludeClientDeprecation: Boolean = false ) { private[caliban] def toCalibanCommonSettings: CalibanCommonSettings = CalibanCommonSettings( @@ -25,7 +26,8 @@ trait Config { imports = imports, splitFiles = Some(splitFiles), enableFmt = Some(enableFmt), - extensibleEnums = Some(extensibleEnums) + extensibleEnums = Some(extensibleEnums), + excludeClientDeprecation = Some(excludeClientDeprecation) ) private[caliban] def asScalaCode: String = { @@ -40,7 +42,8 @@ trait Config { | imports = ${toScalaCode(imports)(v => s""""$v"""")}, | splitFiles = $splitFiles, | enableFmt = $enableFmt, - | extensibleEnums = $extensibleEnums + | extensibleEnums = $extensibleEnums, + | excludeClientDeprecation = $excludeClientDeprecation |) """.stripMargin.trim } diff --git a/tools/src/test/scala/caliban/tools/ClientWriterSpec.scala b/tools/src/test/scala/caliban/tools/ClientWriterSpec.scala index 3d6e18dc9..9b4baebc1 100644 --- a/tools/src/test/scala/caliban/tools/ClientWriterSpec.scala +++ b/tools/src/test/scala/caliban/tools/ClientWriterSpec.scala @@ -43,6 +43,31 @@ object ClientWriterSpec extends DefaultRunnableSpec { ) ) + def genWithoutDeprecation( + schema: String, + scalarMappings: Map[String, String] = Map.empty, + additionalImports: List[String] = List.empty, + extensibleEnums: Boolean = false, + excludeClientDeprecation: Boolean = true + ): RIO[Blocking, String] = Parser + .parseQuery(schema) + .flatMap(doc => + Formatter.format( + ClientWriter + .write( + doc, + additionalImports = Some(additionalImports), + extensibleEnums = extensibleEnums, + excludeClientDeprecation = excludeClientDeprecation + )( + ScalarMappings(Some(scalarMappings)) + ) + .head + ._2, + None + ) + ) + override def spec: ZSpec[TestEnvironment, Any] = suite("ClientWriterSpec")( testM("simple object type") { @@ -558,6 +583,39 @@ bar$tripleQuotes, def name: SelectionBuilder[Character, String] = _root_.caliban.client.SelectionBuilder.Field("name", Scalar()) } +} +""" + ) + ) + }, + testM("deprecated field excluded when cli option is set to true") { + val schema = + """ + type Character { + "name" + name: String! @deprecated(reason: "blah") + nicknames: [String!]! @deprecated + } + """.stripMargin + + assertM(genWithoutDeprecation(schema))( + equalTo( + """import caliban.client.FieldBuilder._ +import caliban.client._ + +object Client { + + type Character + object Character { + + /** + * name + */ + def name: SelectionBuilder[Character, String] = _root_.caliban.client.SelectionBuilder.Field("name", Scalar()) + def nicknames: SelectionBuilder[Character, List[String]] = + _root_.caliban.client.SelectionBuilder.Field("nicknames", ListOf(Scalar())) + } + } """ ) diff --git a/tools/src/test/scala/caliban/tools/OptionsSpec.scala b/tools/src/test/scala/caliban/tools/OptionsSpec.scala index c52494c0a..881eb3b0d 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,6 +296,7 @@ object OptionsSpec extends DefaultRunnableSpec { Some(true), None, None, + None, None ) ) @@ -313,11 +323,39 @@ object OptionsSpec extends DefaultRunnableSpec { None, None, None, + None, None ) ) ) ) + }, + test("provide excludeClientDeprecation") { + val input = List("schema", "output", "--excludeClientDeprecation", "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) + ) + ) + ) + ) } ) } diff --git a/tools/src/test/scala/caliban/tools/compiletime/ConfigSpec.scala b/tools/src/test/scala/caliban/tools/compiletime/ConfigSpec.scala index feacec6c4..b6a5450c6 100644 --- a/tools/src/test/scala/caliban/tools/compiletime/ConfigSpec.scala +++ b/tools/src/test/scala/caliban/tools/compiletime/ConfigSpec.scala @@ -17,7 +17,22 @@ object ConfigSpec extends DefaultRunnableSpec { imports = List("zio.test._", "caliban.tools.compiletime._"), splitFiles = true, enableFmt = false, - extensibleEnums = true + extensibleEnums = true, + excludeClientDeprecation = false + ) + + private val fullExampleWithDeprecation: ClientGenerationSettings = + ClientGenerationSettings( + packageName = "io.example.generated", + clientName = "CalibanClient", + scalafmtPath = Some("a/b/c"), + genView = true, + scalarMappings = List(("mapping key 1", "mapping value 1"), "abc" -> "def"), + imports = List("zio.test._", "caliban.tools.compiletime._"), + splitFiles = true, + enableFmt = false, + extensibleEnums = true, + excludeClientDeprecation = true ) private val toCalibanCommonSettingsSpec = @@ -35,7 +50,30 @@ object ConfigSpec extends DefaultRunnableSpec { imports = List("zio.test._", "caliban.tools.compiletime._"), splitFiles = Some(true), enableFmt = Some(false), - extensibleEnums = Some(true) + extensibleEnums = Some(true), + excludeClientDeprecation = Some(false) + ) + ) + ) + ) + + private val toCalibanCommonSettingsWithDeprecationSpec = + suite("#toCalibanCommonSettingsWithDeprecation")( + test("fullExampleWithDeprecation")( + assertTrue( + fullExampleWithDeprecation.toCalibanCommonSettings == + CalibanCommonSettings( + clientName = Some("CalibanClient"), + scalafmtPath = Some("a/b/c"), + headers = List.empty, + packageName = Some("io.example.generated"), + genView = Some(true), + scalarMappings = List(("mapping key 1", "mapping value 1"), "abc" -> "def"), + imports = List("zio.test._", "caliban.tools.compiletime._"), + splitFiles = Some(true), + enableFmt = Some(false), + extensibleEnums = Some(true), + excludeClientDeprecation = Some(true) ) ) ) @@ -56,7 +94,8 @@ object ConfigSpec extends DefaultRunnableSpec { | imports = List.empty, | splitFiles = false, | enableFmt = true, - | extensibleEnums = false + | extensibleEnums = false, + | excludeClientDeprecation = false |) """.stripMargin.trim ) @@ -74,7 +113,8 @@ object ConfigSpec extends DefaultRunnableSpec { | imports = List("zio.test._","caliban.tools.compiletime._"), | splitFiles = true, | enableFmt = false, - | extensibleEnums = true + | extensibleEnums = true, + | excludeClientDeprecation = false |) """.stripMargin.trim ) @@ -95,7 +135,8 @@ object ConfigSpec extends DefaultRunnableSpec { imports = List.empty, splitFiles = false, enableFmt = true, - extensibleEnums = false + extensibleEnums = false, + excludeClientDeprecation = false ) ) ) @@ -106,7 +147,8 @@ object ConfigSpec extends DefaultRunnableSpec { suite("ClientGenerationSettings")( toCalibanCommonSettingsSpec, asScalaCodeSpec, - defaultSpec + defaultSpec, + toCalibanCommonSettingsWithDeprecationSpec ) ) } diff --git a/vuepress/docs/docs/client.md b/vuepress/docs/docs/client.md index b1846e14f..52de60688 100644 --- a/vuepress/docs/docs/client.md +++ b/vuepress/docs/docs/client.md @@ -92,6 +92,7 @@ The settings available on the `cs` (`CalibanSettings`) builder are: def splitFiles(value: Boolean): CalibanSettings // Split single client object into multiple files (default: false) def enableFmt(value: Boolean): CalibanSettings // Enable code formatting with scalafmt (default: true) def extensibleEnums(value: Boolean): CalibanSettings // Generate a fallback case class for unknown enum values (default: false) + def excludeClientDeprecation(value: Boolean): CalibanSettings // Set it to true to exclude deprecated fields.(default: false) // Only defined for `url` settings, for use in supplying extra headers when fetching the schema itself def headers(pairs: (String,String)*): CalibanSettings @@ -102,7 +103,7 @@ The settings available on the `cs` (`CalibanSettings`) builder are: If you prefer to generate the client explicitly rather than automatically, you can use `calibanGenClient` on the SBT CLI as follows: ```scala -calibanGenClient schemaPath outputPath [--scalafmtPath path] [--headers name:value,name2:value2] [--genView true|false] [--scalarMappings gqlType:f.q.d.n.Type,gqlType2:f.q.d.n.Type2] [--imports a.b.c._,c.d.E] [--splitFiles true|false] [--enableFmt true|false] +calibanGenClient schemaPath outputPath [--scalafmtPath path] [--headers name:value,name2:value2] [--genView true|false] [--scalarMappings gqlType:f.q.d.n.Type,gqlType2:f.q.d.n.Type2] [--imports a.b.c._,c.d.E] [--splitFiles true|false] [--enableFmt true|false] [--excludeClientDeprecation true|false] calibanGenClient project/schema.graphql src/main/client/Client.scala --genView true ``` @@ -124,6 +125,8 @@ This can be helpful with large GraphQL schemas and incremental compilation. Provide `--enableFmt false` option if you don't need to format generated files. Provide `--extensibleEnums true` option if you want to generate a fallback case class for unknown enum values. +Provide `--excludeClientDeprecation true` option if you want to exclude deprecated fields in the schema. Default value is `false` + ### CompileTimeCalibanPlugin As mentioned in the introduction of the [Code Generation](#code-generation) chapter, this "meta" plugin is actually made of two "concrete" sbt plugins, `CompileTimeCalibanServerPlugin` and `CompileTimeCalibanClientPlugin`,