Skip to content

Commit

Permalink
Option to disable default implementation on interface types selection…
Browse files Browse the repository at this point in the history
… builder
  • Loading branch information
AlixBa committed Oct 19, 2021
1 parent 4384751 commit 0d4f08c
Showing 11 changed files with 238 additions and 84 deletions.
Original file line number Diff line number Diff line change
@@ -70,7 +70,7 @@ object CalibanCli {

private val genClientHelpMsg =
s"""
|calibanGenClient schemaPath outputPath [--scalafmtPath path] [--headers name:value,name2:value2] [--packageName name] [--clientName name] [--genView true|false]
|calibanGenClient schemaPath outputPath [--scalafmtPath path] [--headers name:value,name2:value2] [--packageName name] [--clientName name] [--genView true|false] [--defaultOnInterfaceTypes true|false]
|
|This command will create a Scala file in `outputPath` containing client code for all the
|typed defined in the provided GraphQL schema defined at `schemaPath`. Instead of a path,
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ 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 defaultOnInterfaceTypes(value: Boolean): CalibanFileSettings = this.copy(settings = this.settings.defaultOnInterfaceTypes(value))
}

final case class CalibanUrlSettings(url: URL, settings: CalibanCommonSettings) extends CalibanSettings {
@@ -34,4 +35,5 @@ 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 defaultOnInterfaceTypes(value: Boolean): CalibanUrlSettings = this.copy(settings = this.settings.defaultOnInterfaceTypes(value))
}
13 changes: 9 additions & 4 deletions tools/src/main/scala/caliban/tools/CalibanCommonSettings.scala
Original file line number Diff line number Diff line change
@@ -10,7 +10,8 @@ final case class CalibanCommonSettings(
imports: Seq[String],
splitFiles: Option[Boolean],
enableFmt: Option[Boolean],
extensibleEnums: Option[Boolean]
extensibleEnums: Option[Boolean],
defaultOnInterfaceTypes: 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,
defaultOnInterfaceTypes = defaultOnInterfaceTypes
)

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),
defaultOnInterfaceTypes = r.genView.orElse(this.defaultOnInterfaceTypes)
)

def clientName(value: String): CalibanCommonSettings = this.copy(clientName = Some(value))
@@ -56,6 +59,7 @@ 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 defaultOnInterfaceTypes(value: Boolean): CalibanCommonSettings = this.copy(defaultOnInterfaceTypes = Some(value))
}

object CalibanCommonSettings {
@@ -70,6 +74,7 @@ object CalibanCommonSettings {
imports = Seq.empty,
splitFiles = None,
enableFmt = None,
extensibleEnums = None
extensibleEnums = None,
defaultOnInterfaceTypes = None
)
}
45 changes: 27 additions & 18 deletions tools/src/main/scala/caliban/tools/ClientWriter.scala
Original file line number Diff line number Diff line change
@@ -21,7 +21,8 @@ object ClientWriter {
genView: Boolean = false,
additionalImports: Option[List[String]] = None,
splitFiles: Boolean = false,
extensibleEnums: Boolean = false
extensibleEnums: Boolean = false,
defaultOnInterfaceTypes: Boolean = true
)(implicit scalarMappings: ScalarMappings): List[(String, String)] = {
require(packageName.isDefined || !splitFiles, "splitFiles option requires a package name")

@@ -72,7 +73,7 @@ object ClientWriter {
schemaDef.exists(_.subscription.getOrElse("Subscription") == obj.name)
)
.map { typedef =>
val content = writeObject(typedef, genView)
val content = writeObject(typedef, genView, defaultOnInterfaceTypes)
val fullContent =
if (splitFiles)
s"""import caliban.client.FieldBuilder._
@@ -133,7 +134,7 @@ object ClientWriter {
val queries = schema
.objectTypeDefinition(schemaDef.flatMap(_.query).getOrElse("Query"))
.map { typedef =>
val content = writeRootQuery(typedef)
val content = writeRootQuery(typedef, defaultOnInterfaceTypes)
val fullContent =
if (splitFiles)
s"""import caliban.client.FieldBuilder._
@@ -159,7 +160,7 @@ object ClientWriter {
val mutations = schema
.objectTypeDefinition(schemaDef.flatMap(_.mutation).getOrElse("Mutation"))
.map { typedef =>
val content = writeRootMutation(typedef)
val content = writeRootMutation(typedef, defaultOnInterfaceTypes)
val fullContent =
if (splitFiles)
s"""import caliban.client.FieldBuilder._
@@ -185,7 +186,7 @@ object ClientWriter {
val subscriptions = schema
.objectTypeDefinition(schemaDef.flatMap(_.subscription).getOrElse("Subscription"))
.map { typedef =>
val content = writeRootSubscription(typedef)
val content = writeRootSubscription(typedef, defaultOnInterfaceTypes)
val fullContent =
if (splitFiles)
s"""import caliban.client.FieldBuilder._
@@ -290,14 +291,14 @@ object ClientWriter {
): String =
s"type ${typedef.name} = _root_.caliban.client.Operations.RootQuery"

def writeRootQuery(typedef: ObjectTypeDefinition)(implicit
def writeRootQuery(typedef: ObjectTypeDefinition, defaultOnInterfaceTypes: Boolean)(implicit
typesMap: TypesMap,
mappingClashedTypeNames: MappingClashedTypeNames,
scalarMappings: ScalarMappings
): String =
s"""object ${typedef.name} {
| ${typedef.fields
.map(writeField(_, "_root_.caliban.client.Operations.RootQuery", optionalUnion = false))
.map(writeField(_, "_root_.caliban.client.Operations.RootQuery", optionalUnion = false,defaultOnInterfaceTypes))
.mkString("\n ")}
|}
|""".stripMargin
@@ -307,14 +308,14 @@ object ClientWriter {
): String =
s"type ${typedef.name} = _root_.caliban.client.Operations.RootMutation"

def writeRootMutation(typedef: ObjectTypeDefinition)(implicit
def writeRootMutation(typedef: ObjectTypeDefinition, defaultOnInterfaceTypes: Boolean)(implicit
typesMap: TypesMap,
mappingClashedTypeNames: MappingClashedTypeNames,
scalarMappings: ScalarMappings
): String =
s"""object ${typedef.name} {
| ${typedef.fields
.map(writeField(_, "_root_.caliban.client.Operations.RootMutation", optionalUnion = false))
.map(writeField(_, "_root_.caliban.client.Operations.RootMutation", optionalUnion = false, defaultOnInterfaceTypes))
.mkString("\n ")}
|}
|""".stripMargin
@@ -324,14 +325,14 @@ object ClientWriter {
): String =
s"type ${typedef.name} = _root_.caliban.client.Operations.RootSubscription"

def writeRootSubscription(typedef: ObjectTypeDefinition)(implicit
def writeRootSubscription(typedef: ObjectTypeDefinition, defaultOnInterfaceTypes: Boolean)(implicit
typesMap: TypesMap,
mappingClashedTypeNames: MappingClashedTypeNames,
scalarMappings: ScalarMappings
): String =
s"""object ${typedef.name} {
| ${typedef.fields
.map(writeField(_, "_root_.caliban.client.Operations.RootSubscription", optionalUnion = false))
.map(writeField(_, "_root_.caliban.client.Operations.RootSubscription", optionalUnion = false, defaultOnInterfaceTypes))
.mkString("\n ")}
|}
|""".stripMargin
@@ -346,7 +347,7 @@ object ClientWriter {
s"type $objectName"
}

def writeObject(typedef: ObjectTypeDefinition, genView: Boolean)(implicit
def writeObject(typedef: ObjectTypeDefinition, genView: Boolean, defaultOnInterfaceTypes: Boolean)(implicit
typesMap: TypesMap,
mappingClashedTypeNames: MappingClashedTypeNames,
scalarMappings: ScalarMappings
@@ -356,10 +357,10 @@ object ClientWriter {
val unionTypes = typesMap.typesMap.collect { case (key, _: UnionTypeDefinition) => key }
val optionalUnionTypeFields = typedef.fields.flatMap { field =>
val isOptionalUnionType = unionTypes.exists(_.compareToIgnoreCase(field.ofType.toString) == 0)
if (isOptionalUnionType) Some(collectFieldInfo(field, objectName, optionalUnion = true))
if (isOptionalUnionType) Some(collectFieldInfo(field, objectName, optionalUnion = true, defaultOnInterfaceTypes))
else None
}
val fields = typedef.fields.map(collectFieldInfo(_, objectName, optionalUnion = false))
val fields = typedef.fields.map(collectFieldInfo(_, objectName, optionalUnion = false, defaultOnInterfaceTypes))
val view = if (genView) "\n " + writeView(typedef.name, fields.map(_.typeInfo)) else ""

val allFields = fields ++ optionalUnionTypeFields
@@ -611,12 +612,12 @@ object ClientWriter {
private val tripleQuotes = "\"\"\""
private val doubleQuotes = "\""

def writeField(field: FieldDefinition, typeName: String, optionalUnion: Boolean)(implicit
def writeField(field: FieldDefinition, typeName: String, optionalUnion: Boolean, defaultOnInterfaceTypes: Boolean)(implicit
typesMap: TypesMap,
mappingClashedTypeNames: MappingClashedTypeNames,
scalarMappings: ScalarMappings
): String =
writeFieldInfo(collectFieldInfo(field, typeName, optionalUnion))
writeFieldInfo(collectFieldInfo(field, typeName, optionalUnion, defaultOnInterfaceTypes))

def writeFieldInfo(fieldInfo: FieldInfo): String = {
val FieldInfo(
@@ -639,7 +640,7 @@ object ClientWriter {
s"""$description${deprecated}def $safeName$typeParam$args$innerSelection$implicits: SelectionBuilder[$typeName, $outputType] = _root_.caliban.client.SelectionBuilder.Field("$name", $builder$argBuilder)"""
}

def collectFieldInfo(field: FieldDefinition, typeName: String, optionalUnion: Boolean)(implicit
def collectFieldInfo(field: FieldDefinition, typeName: String, optionalUnion: Boolean, defaultOnInterfaceTypes: Boolean)(implicit
typesMap: TypesMap,
mappingClashedTypeNames: MappingClashedTypeNames,
scalarMappings: ScalarMappings
@@ -724,7 +725,15 @@ object ClientWriter {
} else if (interfaceTypes.nonEmpty) {
(
s"[$typeLetter]",
s"(${interfaceTypes.map(t => s"""on${t.name}: Option[SelectionBuilder[${safeTypeName(t.name)}, $typeLetter]] = None""").mkString(", ")})",
s"(${interfaceTypes.map { t =>
val base = s"""on${t.name}: Option[SelectionBuilder[${safeTypeName(t.name)}, $typeLetter]]"""
val default = {
if (defaultOnInterfaceTypes) { " = None" }
else { "" }
}
base + default
}
.mkString(", ")})",
writeType(field.ofType).replace(fieldType, typeLetter),
writeTypeBuilder(
field.ofType,
88 changes: 45 additions & 43 deletions tools/src/main/scala/caliban/tools/Codegen.scala
Original file line number Diff line number Diff line change
@@ -24,50 +24,52 @@ object Codegen {
genType: GenType
): RIO[Blocking, List[File]] =
for {
schema <- loader.load
s = ".*/[scala|play.*|app][^/]*/(.*)/(.*).scala".r.findFirstMatchIn(arguments.toPath)
packageName = arguments.packageName.orElse(s.map(_.group(1).split("/").mkString(".")))
objectName = arguments.clientName.orElse(s.map(_.group(2))).getOrElse("Client")
abstractEffectType = arguments.abstractEffectType.getOrElse(false)
effect = arguments.effect.getOrElse {
if (abstractEffectType) "F" else "zio.UIO"
}
genView = arguments.genView.getOrElse(false)
scalarMappings = arguments.scalarMappings
splitFiles = arguments.splitFiles.getOrElse(false)
enableFmt = arguments.enableFmt.getOrElse(true)
extensibleEnums = arguments.extensibleEnums.getOrElse(false)
code = genType match {
case GenType.Schema =>
List(
objectName -> SchemaWriter.write(schema, packageName, effect, arguments.imports, abstractEffectType)(
ScalarMappings(scalarMappings)
)
)
case GenType.Client =>
ClientWriter.write(
schema,
objectName,
packageName,
genView,
arguments.imports,
splitFiles,
extensibleEnums
)(
ScalarMappings(scalarMappings)
)
}
formatted <- if (enableFmt) Formatter.format(code, arguments.fmtPath) else Task.succeed(code)
paths <- ZIO.foreach(formatted) { case (objectName, objectCode) =>
effectBlocking {
val path = Path.of(
if (splitFiles) s"${arguments.toPath.reverse.dropWhile(_ != '/').reverse}$objectName.scala"
else arguments.toPath
)
schema <- loader.load
s = ".*/[scala|play.*|app][^/]*/(.*)/(.*).scala".r.findFirstMatchIn(arguments.toPath)
packageName = arguments.packageName.orElse(s.map(_.group(1).split("/").mkString(".")))
objectName = arguments.clientName.orElse(s.map(_.group(2))).getOrElse("Client")
abstractEffectType = arguments.abstractEffectType.getOrElse(false)
effect = arguments.effect.getOrElse {
if (abstractEffectType) "F" else "zio.UIO"
}
genView = arguments.genView.getOrElse(false)
scalarMappings = arguments.scalarMappings
splitFiles = arguments.splitFiles.getOrElse(false)
enableFmt = arguments.enableFmt.getOrElse(true)
extensibleEnums = arguments.extensibleEnums.getOrElse(false)
defaultOnInterfaceTypes = arguments.defaultOnInterfaceTypes.getOrElse(false)
code = genType match {
case GenType.Schema =>
List(
objectName -> SchemaWriter.write(schema, packageName, effect, arguments.imports, abstractEffectType)(
ScalarMappings(scalarMappings)
)
)
case GenType.Client =>
ClientWriter.write(
schema,
objectName,
packageName,
genView,
arguments.imports,
splitFiles,
extensibleEnums,
defaultOnInterfaceTypes
)(
ScalarMappings(scalarMappings)
)
}
formatted <- if (enableFmt) Formatter.format(code, arguments.fmtPath) else Task.succeed(code)
paths <- ZIO.foreach(formatted) { case (objectName, objectCode) =>
effectBlocking {
val path = Path.of(
if (splitFiles) s"${arguments.toPath.reverse.dropWhile(_ != '/').reverse}$objectName.scala"
else arguments.toPath
)

Files.writeString(path, objectCode, StandardCharsets.UTF_8).toFile
}
}
Files.writeString(path, objectCode, StandardCharsets.UTF_8).toFile
}
}
} yield paths

def generate(arguments: Options, genType: GenType): RIO[Blocking, List[File]] =
9 changes: 6 additions & 3 deletions tools/src/main/scala/caliban/tools/Options.scala
Original file line number Diff line number Diff line change
@@ -17,7 +17,8 @@ final case class Options(
abstractEffectType: Option[Boolean],
splitFiles: Option[Boolean],
enableFmt: Option[Boolean],
extensibleEnums: Option[Boolean]
extensibleEnums: Option[Boolean],
defaultOnInterfaceTypes: 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],
defaultOnInterfaceTypes: 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.defaultOnInterfaceTypes
)
}
case _ => None
9 changes: 6 additions & 3 deletions tools/src/main/scala/caliban/tools/compiletime/Config.scala
Original file line number Diff line number Diff line change
@@ -12,7 +12,8 @@ trait Config {
imports: List[String] = List.empty,
splitFiles: Boolean = false,
enableFmt: Boolean = true,
extensibleEnums: Boolean = false
extensibleEnums: Boolean = false,
defaultOnInterfaceTypes: Boolean = true
) {
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),
defaultOnInterfaceTypes = Some(defaultOnInterfaceTypes)
)

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,
| defaultOnInterfaceTypes = $defaultOnInterfaceTypes
|)
""".stripMargin.trim
}
Loading

0 comments on commit 0d4f08c

Please sign in to comment.