Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to split the single client file into multiple files. #963

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions codegen-sbt/src/main/scala/caliban/codegen/CalibanSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ sealed trait CalibanSettings {
def genView: Option[Boolean]
def scalarMappings: Seq[(String, String)]
def imports: Seq[String]
def splitFiles: Option[Boolean]
def enableFmt: Option[Boolean]

def append(other: Type): Type
}
Expand All @@ -24,7 +26,9 @@ case class CalibanFileSettings(
packageName: Option[String],
genView: Option[Boolean],
scalarMappings: Seq[(String, String)],
imports: Seq[String]
imports: Seq[String],
splitFiles: Option[Boolean],
enableFmt: Option[Boolean]
) extends CalibanSettings {
type Type = CalibanFileSettings
val headers = Seq.empty // Not applicable for file generator
Expand All @@ -37,7 +41,9 @@ case class CalibanFileSettings(
packageName = other.packageName.orElse(packageName),
genView = other.genView.orElse(genView),
scalarMappings = scalarMappings ++ other.scalarMappings,
imports = imports ++ other.imports
imports = imports ++ other.imports,
splitFiles = other.splitFiles.orElse(splitFiles),
enableFmt = other.enableFmt.orElse(enableFmt)
)

def clientName(value: String): CalibanFileSettings = this.copy(clientName = Some(value))
Expand All @@ -47,6 +53,8 @@ case class CalibanFileSettings(
def scalarMapping(mapping: (String, String)*): CalibanFileSettings =
this.copy(scalarMappings = this.scalarMappings ++ mapping)
def imports(values: String*): CalibanFileSettings = this.copy(imports = this.imports ++ values)
def splitFiles(value: Boolean): CalibanFileSettings = this.copy(splitFiles = Some(value))
def enableFmt(value: Boolean): CalibanFileSettings = this.copy(enableFmt = Some(value))
}

case class CalibanUrlSettings(
Expand All @@ -57,7 +65,9 @@ case class CalibanUrlSettings(
packageName: Option[String],
genView: Option[Boolean],
scalarMappings: Seq[(String, String)],
imports: Seq[String]
imports: Seq[String],
splitFiles: Option[Boolean],
enableFmt: Option[Boolean]
) extends CalibanSettings {
type Type = CalibanUrlSettings
def append(other: CalibanUrlSettings): CalibanUrlSettings =
Expand All @@ -69,7 +79,9 @@ case class CalibanUrlSettings(
packageName = other.packageName.orElse(packageName),
genView = other.genView.orElse(genView),
scalarMappings = scalarMappings ++ other.scalarMappings,
imports = imports ++ other.imports
imports = imports ++ other.imports,
splitFiles = other.splitFiles.orElse(splitFiles),
enableFmt = other.enableFmt.orElse(enableFmt)
)

def clientName(value: String): CalibanUrlSettings = this.copy(clientName = Some(value))
Expand All @@ -81,6 +93,8 @@ case class CalibanUrlSettings(
def scalarMapping(mapping: (String, String)*): CalibanUrlSettings =
this.copy(scalarMappings = this.scalarMappings ++ mapping)
def imports(values: String*): CalibanUrlSettings = this.copy(imports = this.imports ++ values)
def splitFiles(value: Boolean): CalibanUrlSettings = this.copy(splitFiles = Some(value))
def enableFmt(value: Boolean): CalibanUrlSettings = this.copy(enableFmt = Some(value))
}

object CalibanSettings {
Expand All @@ -92,7 +106,9 @@ object CalibanSettings {
packageName = Option.empty[String],
genView = Option.empty[Boolean],
scalarMappings = Seq.empty[(String, String)],
imports = Seq.empty[String]
imports = Seq.empty[String],
splitFiles = Option.empty[Boolean],
enableFmt = Option.empty[Boolean]
)

def emptyUrl(url: URL): CalibanUrlSettings = CalibanUrlSettings(
Expand All @@ -103,6 +119,8 @@ object CalibanSettings {
packageName = Option.empty[String],
genView = Option.empty[Boolean],
scalarMappings = Seq.empty[(String, String)],
imports = Seq.empty[String]
imports = Seq.empty[String],
splitFiles = Option.empty[Boolean],
enableFmt = Option.empty[Boolean]
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,15 @@ object CalibanSourceGenerator {
) // NB: Presuming zio-config can read toString'd booleans
val scalarMappings = pairList("--scalarMappings", settings.scalarMappings)
val imports = list("--imports", settings.imports)

scalafmtPath ++ headers ++ packageName ++ genView ++ scalarMappings ++ imports
val splitFiles = singleOpt(
"--splitFiles",
settings.splitFiles.map(_.toString())
) // NB: Presuming zio-config can read toString'd booleans
val enableFmt = singleOpt(
"--enableFmt",
settings.enableFmt.map(_.toString())
) // NB: Presuming zio-config can read toString'd booleans
scalafmtPath ++ headers ++ packageName ++ genView ++ scalarMappings ++ imports ++ splitFiles ++ enableFmt
}

def apply(
Expand Down
8 changes: 5 additions & 3 deletions codegen-sbt/src/sbt-test/codegen/test-compile/test
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ $ mkdir src/main/scala/genview/client
> calibanGenClient project/schema-to-check-name-uniqueness.graphql src/main/scala/genview/client/ClientNameUniqueness.scala --packageName genview.client --genView true
$ exists src/main/scala/genview/client/ClientNameUniqueness.scala
$ exec sh verify.sh StarshipView ./src/main/scala/genview/client/ClientNameUniqueness.scala
> calibanGenClient project/gitlab-schema.graphql src/main/scala/genview/client/ClientGitLab.scala --packageName genview.client --genView true
$ exists src/main/scala/genview/client/ClientGitLab.scala
$ exec sh verify.sh VulnerableProjectsByGradeView ./src/main/scala/genview/client/ClientGitLab.scala
> calibanGenClient project/gitlab-schema.graphql src/main/scala/genview/client/ClientGitLab.scala --packageName genview.client --genView true --splitFiles true --enableFmt false
$ exists src/main/scala/genview/client/package.scala
$ exec sh verify.sh VulnerableProjectsByGrade ./src/main/scala/genview/client/package.scala
$ exists src/main/scala/genview/client/VulnerableProjectsByGrade.scala
$ exec sh verify.sh VulnerableProjectsByGradeView ./src/main/scala/genview/client/VulnerableProjectsByGrade.scala
> compile

$ exists target/scala-2.12/src_managed/main/caliban-codegen-sbt/caliban/Client.scala
Expand Down
10 changes: 8 additions & 2 deletions docs/docs/client.html
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,12 @@
def genView(value: Boolean): CalibanSettings // Provide a case class and helper method to select all fields on an object (default: false)
def scalarMapping(mapping: (String,String)*): CalibanSettings // A mapping from GraphQL scalar types to JVM types, as unknown scalar types are represented as String by default.
def imports(values: String*): CalibanSettings // A list of imports to be added to the top of a generated client
def splitFiles(value: Boolean): CalibanSettings // Split single client object into multiple files (default: false)
alexdupre marked this conversation as resolved.
Show resolved Hide resolved
def enableFmt(value: Boolean): CalibanSettings // Enable code formatting with scalafmt (default: true)

// Only defined for `url` settings, for use in supplying extra headers when fetching the schema itself
def headers(pairs: (String,String)*): CalibanSettings
</code></pre></div><h3 id="calibangenclient"><a href="#calibangenclient" class="header-anchor">#</a> <code>calibanGenClient</code></h3> <p>If you prefer to generate the client explicitly rather than automatically, you can use <code>calibanGenClient</code> on the SBT CLI as follows:</p> <div class="language-scala extra-class"><pre class="language-scala"><code>calibanGenClient schemaPath outputPath <span class="token punctuation">[</span><span class="token operator">--</span>scalafmtPath path<span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token operator">--</span>headers name<span class="token operator">:</span>value<span class="token punctuation">,</span>name2<span class="token operator">:</span>value2<span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token operator">--</span>genView <span class="token boolean">true</span><span class="token operator">|</span><span class="token boolean">false</span><span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token operator">--</span>scalarMappings gqlType<span class="token operator">:</span>f<span class="token punctuation">.</span>q<span class="token punctuation">.</span>d<span class="token punctuation">.</span>n<span class="token punctuation">.</span>Type<span class="token punctuation">,</span>gqlType2<span class="token operator">:</span>f<span class="token punctuation">.</span>q<span class="token punctuation">.</span>d<span class="token punctuation">.</span>n<span class="token punctuation">.</span>Type2<span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token operator">--</span>imports a<span class="token punctuation">.</span>b<span class="token punctuation">.</span>c<span class="token punctuation">.</span>_<span class="token punctuation">,</span>c<span class="token punctuation">.</span>d<span class="token punctuation">.</span>E<span class="token punctuation">]</span>
</code></pre></div><h3 id="calibangenclient"><a href="#calibangenclient" class="header-anchor">#</a> <code>calibanGenClient</code></h3> <p>If you prefer to generate the client explicitly rather than automatically, you can use <code>calibanGenClient</code> on the SBT CLI as follows:</p> <div class="language-scala extra-class"><pre class="language-scala"><code>calibanGenClient schemaPath outputPath <span class="token punctuation">[</span><span class="token operator">--</span>scalafmtPath path<span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token operator">--</span>headers name<span class="token operator">:</span>value<span class="token punctuation">,</span>name2<span class="token operator">:</span>value2<span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token operator">--</span>genView <span class="token boolean">true</span><span class="token operator">|</span><span class="token boolean">false</span><span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token operator">--</span>scalarMappings gqlType<span class="token operator">:</span>f<span class="token punctuation">.</span>q<span class="token punctuation">.</span>d<span class="token punctuation">.</span>n<span class="token punctuation">.</span>Type<span class="token punctuation">,</span>gqlType2<span class="token operator">:</span>f<span class="token punctuation">.</span>q<span class="token punctuation">.</span>d<span class="token punctuation">.</span>n<span class="token punctuation">.</span>Type2<span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token operator">--</span>imports a<span class="token punctuation">.</span>b<span class="token punctuation">.</span>c<span class="token punctuation">.</span>_<span class="token punctuation">,</span>c<span class="token punctuation">.</span>d<span class="token punctuation">.</span>E<span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token operator">--</span>splitFiles <span class="token boolean">true</span><span class="token operator">|</span><span class="token boolean">false</span><span class="token punctuation">]</span> <span class="token punctuation">[</span><span class="token operator">--</span>enableFmt <span class="token boolean">true</span><span class="token operator">|</span><span class="token boolean">false</span><span class="token punctuation">]</span>

calibanGenClient project<span class="token operator">/</span>schema<span class="token punctuation">.</span>graphql src<span class="token operator">/</span>main<span class="token operator">/</span>client<span class="token operator">/</span>Client<span class="token punctuation">.</span>scala <span class="token operator">--</span>genView <span class="token boolean">true</span>
</code></pre></div><p>This command will generate a Scala file in <code>outputPath</code> containing helper functions for all the types defined in the provided GraphQL schema defined at <code>schemaPath</code>.
Expand All @@ -80,7 +82,11 @@
option.
Provide <code>--genView true</code> option if you want to generate a view for the GraphQL types.
If you want to force a mapping between a GraphQL type and a Scala class (such as scalars), you can use the
<code>--scalarMappings</code> option. Also you can add imports for example for your ArgEncoder implicits by providing <code>--imports</code> option.</p> <h2 id="query-building"><a href="#query-building" class="header-anchor">#</a> Query building</h2> <p>Once the boilerplate code is generated, you can start building queries. For each <em>type</em> in your schema, a corresponding Scala object has been created. For each <em>field</em> in your schema, a corresponding Scala function has been created.</p> <p>For example, given the following schema:</p> <div class="language-graphql extra-class"><pre class="language-graphql"><code><span class="token keyword">type</span> <span class="token class-name">Character</span> <span class="token punctuation">{</span>
<code>--scalarMappings</code> option. Also you can add imports for example for your ArgEncoder implicits by providing <code>--imports</code> option.
Use the <code>--splitFiles true</code> option if you want to generate multiple files within the same package instead of a single file.
In this case the filename part of the <code>outputPath</code> will be ignored, but the value will still be used to determine the mandatory package name and destination directory.
This can be helpful with large schemas and incremental compilation.
Provide <code>--enableFmt</code> option if you don't need to format generated files.</p> <h2 id="query-building"><a href="#query-building" class="header-anchor">#</a> Query building</h2> <p>Once the boilerplate code is generated, you can start building queries. For each <em>type</em> in your schema, a corresponding Scala object has been created. For each <em>field</em> in your schema, a corresponding Scala function has been created.</p> <p>For example, given the following schema:</p> <div class="language-graphql extra-class"><pre class="language-graphql"><code><span class="token keyword">type</span> <span class="token class-name">Character</span> <span class="token punctuation">{</span>
<span class="token attr-name">name</span><span class="token punctuation">:</span> <span class="token scalar">String</span><span class="token operator">!</span>
<span class="token attr-name">nicknames</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token scalar">String</span><span class="token operator">!</span><span class="token punctuation">]</span><span class="token operator">!</span>
<span class="token attr-name">origin</span><span class="token punctuation">:</span> <span class="token class-name">Origin</span><span class="token operator">!</span>
Expand Down
Loading