root
: Contains tests and nothing elsecodegen
: Codegen core. Defines algebras and default interpreters.sample
: Contains integration tests for generated code. May be generated viaexample
in the sbt console.
- Readability
- Maintainability
- Testability
- Defer effects
- Typesafe accessors for Java function calls
It is difficult to tell if the code generated by the tool is functional and type sound by structure alone. As unit tests are not enough, integration tests actually run guardrail as a CLI tool to generate complete clients and servers, ensuring everything works as expected.
The project defined at modules/sample
is divided into two parts:
src/main
: Completely generated code,.gitignore
explicitly prevents these files from being tracked.src/test
: Manually written integration tests against the generated code. These tests are run by thetestSuite
command.
By running guardrail, then attempting to compile the generated code, then running integration tests against the generated code, we can ensure quality.
Adding new specifications is accomplished by:
- creating a file in
modules/sample/src/main/resources
- adding an entry in
runExample
defined inbuild.sbt
. The available flags are largely undocumented, so reading the parser is necessary.
lazy val runExample: TaskKey[Unit] = taskKey[Unit]("Run with example args")
fullRunTask(
runExample,
Test,
"com.twilio.guardrail.CLI",
"""
--defaults --import support.PositiveLong
--client --specPath modules/sample/src/main/resources/petstore.json --outputPath modules/sample/src/main/scala --packageName clients.http4s --framework http4s
--client --specPath modules/sample/src/main/resources/edgecases/defaults.yaml --outputPath modules/sample/src/main/scala --packageName edgecases.defaults
--client --specPath modules/sample/src/main/resources/custom-header-type.yaml --outputPath modules/sample/src/main/scala --packageName tests.customTypes.customHeader
""".replaceAllLiterally("\n", " ").split(' ').filter(_.nonEmpty): _*
)
--specPath
has to point to the newly added specification file--outputPath
must point tomodules/sample/src/main/scala
--packageName
a unique, semantic name for your generated files. Good names:issues.issue42
frameworks.akka.fileUploader
Define your tests in ./modules/sample/src/test/scala
make sure to use
imports corresponding the previously defined packageName
Use the example
command inside of an SBT session to run code generation and execute the tests
testSuite
: Compile, test codegen, run sample codegen, compile sample, run tests inside sampleruntimeSuite
: Run guardrail, then run all tests against the generated codecli
: Useful for scripting:sbt 'cli --client ...'
format
: Runs scalafmt against codebasecheckFormatting
: Verifies formatting, run as part of CI against PRs
- Cats' Free Monad: http://typelevel.org/cats/datatypes/freemonad.html
- Scalameta Quasiquotes: https://github.com/scalameta/scalameta/blob/master/notes/quasiquotes.md