Skip to content

Commit

Permalink
Support multiple setting sets per file (ghostdogpr#1156)
Browse files Browse the repository at this point in the history
  • Loading branch information
kubukoz authored and Fluxx committed Jan 27, 2022
1 parent 155c7d4 commit 2b83114
Show file tree
Hide file tree
Showing 12 changed files with 130 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package caliban.codegen

import _root_.caliban.tools._
import sbt._
import sjsonnew.IsoLList.Aux
import sjsonnew.IsoLList
import zio.blocking.Blocking

import java.io.File
Expand All @@ -22,11 +22,11 @@ object CalibanSourceGenerator {
fileSettings: Seq[CalibanFileSettings],
urlSettings: Seq[CalibanUrlSettings]
): TrackedSettings = {
val allSettings: Seq[CalibanSettings] = sources.toList.map(collectSettingsFor(fileSettings, _)) ++ urlSettings
val allSettings: Seq[CalibanSettings] = sources.toList.flatMap(collectSettingsFor(fileSettings, _)) ++ urlSettings
TrackedSettings(allSettings.map(_.toString))
}

implicit val analysisIso: Aux[TrackedSettings, Seq[String] :*: LNil] =
implicit val analysisIso: IsoLList.Aux[TrackedSettings, Seq[String] :*: LNil] =
LList.iso[TrackedSettings, Seq[String] :*: LNil](
{ case TrackedSettings(arguments) => ("args", arguments) :*: LNil },
{ case (_, args) :*: LNil => TrackedSettings(args) }
Expand All @@ -45,17 +45,19 @@ object CalibanSourceGenerator {
interimPath.getParent.resolve(scalaName).toFile
}

def collectSettingsFor(fileSettings: Seq[CalibanFileSettings], source: File): CalibanFileSettings = {
def collectSettingsFor(fileSettings: Seq[CalibanFileSettings], source: File): Seq[CalibanFileSettings] = {
// Supply a default packageName.
// If we do not, `src_managed.main.caliban-codegen-sbt` will be used,
// which is not only terrible, but invalid.
val defaults: CalibanCommonSettings = CalibanCommonSettings.empty.copy(packageName = Some("caliban"))

CalibanFileSettings(
file = source,
settings = fileSettings.collect { case needle if source.toPath.endsWith(needle.file.toPath) => needle }
.foldLeft[CalibanCommonSettings](defaults) { case (acc, next) => acc.combine(next.settings) }
)
val matchingSettingSets = fileSettings.collect {
case needle if source.toPath.endsWith(needle.file.toPath) => needle.settings
}.map(defaults.combine(_))

val finalSettingSets = if (matchingSettingSets.isEmpty) List(defaults) else matchingSettingSets

finalSettingSets.map(settings => CalibanFileSettings(file = source, settings = settings))
}

def apply(
Expand Down Expand Up @@ -94,25 +96,31 @@ object CalibanSourceGenerator {
files <- Codegen.generate(opts, settings.genType).asSomeError
} yield files

Runtime.default
.unsafeRun(
for {
fromFiles <- ZIO.foreach(sources.toList)(source =>
generateFileSource(source, collectSettingsFor(fileSettings, source).settings).catchAll {
case Some(reason) =>
putStrLn(reason.toString) *> putStrLn(reason.getStackTrace.mkString("\n")).as(List.empty)
case None => ZIO.succeed(List.empty)
}
)
fromUrls <- ZIO.foreach(urlSettings)(setting =>
generateUrlSource(setting.url, setting.settings).catchAll {
case Some(reason) =>
putStrLn(reason.toString) *> putStrLn(reason.getStackTrace.mkString("\n")).as(List.empty)
case None => ZIO.succeed(List.empty)
}
)
} yield (fromFiles ++ fromUrls).flatten
)
val generateFromFiles = ZIO
.foreach(sources.toList) { source =>
ZIO
.collectAll(
collectSettingsFor(fileSettings, source).map(s => generateFileSource(source, s.settings))
)
.catchAll {
case Some(reason) =>
putStrLn(reason.toString) *> putStrLn(reason.getStackTrace.mkString("\n")).as(List.empty)
case None => ZIO.succeed(List.empty)
}
.map(_.flatten)
}

val generateFromURLs = ZIO.foreach(urlSettings)(setting =>
generateUrlSource(setting.url, setting.settings).catchAll {
case Some(reason) =>
putStrLn(reason.toString) *> putStrLn(reason.getStackTrace.mkString("\n")).as(List.empty)
case None => ZIO.succeed(List.empty)
}
)

Runtime.default.unsafeRun {
ZIO.mapN(generateFromFiles, generateFromURLs)((_ ++ _)).map(_.flatten)
}
}

// NB: This is heavily inspired by the caching technique from eed3si9n's sbt-scalaxb plugin
Expand Down
8 changes: 8 additions & 0 deletions codegen-sbt/src/sbt-test/codegen/gen-client-task/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
lazy val root = project
.in(file("."))
.enablePlugins(CodegenPlugin) // Intentionally maintain the deprecated name
.settings(
libraryDependencies ++= Seq(
"com.github.ghostdogpr" %% "caliban-client" % Version.pluginVersion
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
object Version {
def pluginVersion: String =
sys.props.get("plugin.version") match {
case Some(x) => x
case _ => sys.error("""|The system property 'plugin.version' is not defined.
|Specify this property using the scriptedLaunchOpts -D.""".stripMargin)
}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
sys.props.get("plugin.version") match {
case Some(x) => addSbtPlugin("com.github.ghostdogpr" % "caliban-codegen-sbt" % x)
case _ => sys.error("""|The system property 'plugin.version' is not defined.
|Specify this property using the scriptedLaunchOpts -D.""".stripMargin)
}
34 changes: 34 additions & 0 deletions codegen-sbt/src/sbt-test/codegen/gen-client-task/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
$ absent src/main/scala/client/ClientNameUniqueness.scala
$ absent src/main/scala/client/ClientGitLab.scala
$ absent app/com/caliban/client/ClientPlayFramework.scala
$ absent play23/com/caliban/client/ClientPlayFramework.scala

$ mkdir src/main/scala
$ mkdir src/main/scala/client
$ mkdir app/com/caliban/client
$ mkdir play23/com/caliban/client

> calibanGenClient project/schema-to-check-name-uniqueness.graphql src/main/scala/client/ClientNameUniqueness.scala --packageName client
$ exists src/main/scala/client/ClientNameUniqueness.scala

> calibanGenClient project/schema-to-check-name-uniqueness.graphql app/com/caliban/client/ClientPlayFramework.scala --packageName client
$ exists app/com/caliban/client/ClientPlayFramework.scala
$ exec sh verify.sh ClientPlayFramework ./app/com/caliban/client/ClientPlayFramework.scala

> calibanGenClient project/schema-to-check-name-uniqueness.graphql play23/com/caliban/client/ClientPlayFramework.scala --packageName client
$ exists play23/com/caliban/client/ClientPlayFramework.scala
$ exec sh verify.sh ClientPlayFramework ./play23/com/caliban/client/ClientPlayFramework.scala

$ mkdir src/main/scala/genview
$ 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 --splitFiles true --enableFmt false
$ exists src/main/scala/genview/client/package.scala
$ exec sh verify.sh ProjectID ./src/main/scala/genview/client/package.scala
$ exists src/main/scala/genview/client/Project.scala
$ exec sh verify.sh ProjectView ./src/main/scala/genview/client/Project.scala
$ exec sh verify.sh ProjectViewArgs ./src/main/scala/genview/client/Project.scala
$ exec sh verify.sh ProjectViewSelectionArgs ./src/main/scala/genview/client/Project.scala
9 changes: 9 additions & 0 deletions codegen-sbt/src/sbt-test/codegen/gen-client-task/verify.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash

if grep -q "$1" "$2"; then
echo "$1 exists in $2"
exit 0
else
echo "$1 is missing in $2"
exit 1
fi
20 changes: 12 additions & 8 deletions codegen-sbt/src/sbt-test/codegen/test-compile/build.sbt
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import _root_.caliban.tools.Codegen

lazy val root = project
.in(file("."))
.enablePlugins(CodegenPlugin) // Intentionally maintain the deprecated name
.enablePlugins(CalibanPlugin)
.settings(
libraryDependencies ++= Seq(
"com.github.ghostdogpr" %% "caliban-client" % Version.pluginVersion
),
Compile / caliban / calibanSettings ++= Seq(
calibanSetting(file("src/main/graphql/schema.graphql"))( // Explicitly constrain to disambiguate
cs =>
cs.clientName("Client")
calibanSetting(file("src/main/graphql/schema.graphql"))( // Explicitly constrain to disambiguate
_.clientName("Client")
),
// Another entry for the same file, which will cause another generator to run
calibanSetting(file("src/main/graphql/schema.graphql"))(
_.genType(Codegen.GenType.Schema)
.scalarMapping("Json" -> "String")
.effect("scala.util.Try")
),
calibanSetting(file("src/main/graphql/genview/schema.graphql"))(
cs =>
cs.clientName("Client")
.packageName("genview")
.genView(true)
_.clientName("Client").packageName("genview").genView(true)
)
)
)
35 changes: 1 addition & 34 deletions codegen-sbt/src/sbt-test/codegen/test-compile/test
Original file line number Diff line number Diff line change
@@ -1,39 +1,6 @@
$ absent src/main/scala/client/ClientNameUniqueness.scala
$ absent src/main/scala/client/ClientGitLab.scala
$ absent app/com/caliban/client/ClientPlayFramework.scala
$ absent play23/com/caliban/client/ClientPlayFramework.scala

$ mkdir src/main/scala
$ mkdir src/main/scala/client
$ mkdir app/com/caliban/client
$ mkdir play23/com/caliban/client

> calibanGenClient project/schema-to-check-name-uniqueness.graphql src/main/scala/client/ClientNameUniqueness.scala --packageName client
$ exists src/main/scala/client/ClientNameUniqueness.scala

> calibanGenClient project/schema-to-check-name-uniqueness.graphql app/com/caliban/client/ClientPlayFramework.scala --packageName client
$ exists app/com/caliban/client/ClientPlayFramework.scala
$ exec sh verify.sh ClientPlayFramework ./app/com/caliban/client/ClientPlayFramework.scala

> calibanGenClient project/schema-to-check-name-uniqueness.graphql play23/com/caliban/client/ClientPlayFramework.scala --packageName client
$ exists play23/com/caliban/client/ClientPlayFramework.scala
$ exec sh verify.sh ClientPlayFramework ./play23/com/caliban/client/ClientPlayFramework.scala

$ mkdir src/main/scala/genview
$ 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 --splitFiles true --enableFmt false
$ exists src/main/scala/genview/client/package.scala
$ exec sh verify.sh ProjectID ./src/main/scala/genview/client/package.scala
$ exists src/main/scala/genview/client/Project.scala
$ exec sh verify.sh ProjectView ./src/main/scala/genview/client/Project.scala
$ exec sh verify.sh ProjectViewArgs ./src/main/scala/genview/client/Project.scala
$ exec sh verify.sh ProjectViewSelectionArgs ./src/main/scala/genview/client/Project.scala
> compile

$ exists target/scala-2.12/src_managed/main/caliban-codegen-sbt/caliban/Client.scala
$ exists target/scala-2.12/src_managed/main/caliban-codegen-sbt/genview/Client.scala
$ exists target/scala-2.12/src_managed/main/caliban-codegen-sbt/caliban/schema.scala
$ exec sh verify.sh CharacterView target/scala-2.12/src_managed/main/caliban-codegen-sbt/genview/Client.scala
16 changes: 15 additions & 1 deletion vuepress/docs/docs/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Caliban provides two sbt plugins to generate your client(s) code.

The first one, named `CalibanPlugin`, allows you to generate the client code from a schema file or from a server URL.

The second one, named `CompileTimeCalibanPlugin`, allows you to generate the client code from your server code.
The second one, named `CompileTimeCalibanPlugin`, allows you to generate the client code from your server code.
This second "meta" plugin is actually made of two "concrete" plugins, `CompileTimeCalibanServerPlugin` and `CompileTimeCalibanClientPlugin`, that you'll
both need to configure in your project to be able to generate you Caliban client code from your Caliban server code.

Expand Down Expand Up @@ -69,6 +69,20 @@ In order to supply more configuration options to the code generator, you can use
)
```

The path where the generator will look for schemas can be customized by overriding the `calibanSources` settings:

```scala
Compile / caliban / calibanSources := file("caliban")
```

If you want to cherry-pick certain files yourself, you can override that as well with an explicit `caliban / sources` entry:

```scala
Compile / caliban / sources := List(file("caliban") / "Service.graphql")
```

For every entry in `calibanSettings` for the same file, a separate client (or [schema](schema.md#code-generation), depending on the entry's `genType` value) will be generated.

#### From a server URL

The `calibanSetting` function also permits generating clients for supplied `url`'s:
Expand Down
3 changes: 3 additions & 0 deletions vuepress/docs/docs/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,9 @@ You can also indicate that the effect type is abstract via `--abstractEffectType
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.

Since Caliban 1.3.0, you can generate schemas using an sbt `sourceGenerator`, which means your schemas will be generated every time you compile (or when you import your build into [Metals](https://scalameta.org/metals/)).
This can be configured with the same settings as [the client generators](client.md#code-generation), but you have to specify `.genType(Codegen.GenType.Schema)` in the `calibanSettings` entry for a given file.

## Building Schemas by hand

Sometimes for whatever reason schema generation fails. This can happen if your schema has co-recursive types and Magnolia is unable
Expand Down

0 comments on commit 2b83114

Please sign in to comment.