diff --git a/project/Dependencies.scala b/project/Dependencies.scala
index 5c3cf37c..017e3f1b 100644
--- a/project/Dependencies.scala
+++ b/project/Dependencies.scala
@@ -83,6 +83,7 @@ object Dependencies {
val tapirHttp4s = "com.softwaremill.sttp.tapir" %% "tapir-http4s-server" % V.tapir
val munit = "org.scalameta" %% "munit" % V.munit
val munitScalaCheck = "org.scalameta" %% "munit-scalacheck" % V.munit
+ val log4j = "org.apache.logging.log4j" % "log4j-api" % "2.13.1"
val enumeroDependencies = List(
munit,
@@ -186,11 +187,14 @@ object Dependencies {
val tapiroCoreDependencies = List(
sbtLogging,
+ log4j,
scalameta,
scalafmtCore,
circeCore,
pprint,
- )
+ ) ++ List(
+ munit,
+ ).map(_ % Test)
val docsDependencies = List(
plantuml,
diff --git a/tapiro/ci/test.sh b/tapiro/ci/test.sh
index b7a11e96..d6266686 100755
--- a/tapiro/ci/test.sh
+++ b/tapiro/ci/test.sh
@@ -4,4 +4,4 @@ set -e
apk add --no-cache curl
-sbt -batch ';tapiroCore/compile ;sbt-tapiro/scripted' # TODO(claudio): compile to test once we have them
+sbt -batch ';tapiroCore/test ;sbt-tapiro/scripted'
diff --git a/tapiro/core/src/main/scala/io/buildo/tapiro/Util.scala b/tapiro/core/src/main/scala/io/buildo/tapiro/Util.scala
index 563b7620..aceb3fd5 100644
--- a/tapiro/core/src/main/scala/io/buildo/tapiro/Util.scala
+++ b/tapiro/core/src/main/scala/io/buildo/tapiro/Util.scala
@@ -12,12 +12,9 @@ import scala.meta._
import scala.util.control.NonFatal
import java.nio.file.Paths
import java.nio.file.Files
-
import cats.data.NonEmptyList
-
import MetarpheusHelper._
-
-import sbt.internal.util.ManagedLogger
+import org.apache.logging.log4j.LogManager
import Meta.typeNameString
@@ -36,9 +33,11 @@ object TapiroRouteError {
case class TapiroRoute(route: Route, error: TapiroRouteError)
-class Util(logger: ManagedLogger) {
+class Util() {
import Formatter.format
+ val logger = LogManager.getLogger("io.buildo.tapiro")
+
def createFiles(
routesPaths: List[String],
modelsPaths: List[String],
diff --git a/tapiro/core/src/test/resources/log4j2-test.xml b/tapiro/core/src/test/resources/log4j2-test.xml
new file mode 100644
index 00000000..c36ec960
--- /dev/null
+++ b/tapiro/core/src/test/resources/log4j2-test.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/tapiro/core/src/test/scala/io/buildo/tapiro/FileLayout.scala b/tapiro/core/src/test/scala/io/buildo/tapiro/FileLayout.scala
new file mode 100644
index 00000000..d5382e43
--- /dev/null
+++ b/tapiro/core/src/test/scala/io/buildo/tapiro/FileLayout.scala
@@ -0,0 +1,58 @@
+package io.buildo.tapiro
+
+import java.nio.charset.Charset
+import java.nio.charset.StandardCharsets
+import java.nio.file.Files
+import java.nio.file.StandardOpenOption
+import scala.meta.io.AbsolutePath
+
+//TODO(gabro): add Metals notice
+object FileLayout {
+
+ def mapFromString(layout: String): Map[String, String] = {
+ if (!layout.trim.isEmpty) {
+ val lines = layout.replaceAllLiterally("\r\n", "\n")
+ lines
+ .split("(?=\n/[^/])")
+ .map { row =>
+ row.stripPrefix("\n").split("\n", 2).toList match {
+ case path :: contents :: Nil =>
+ path.stripPrefix("/") -> contents
+ case els =>
+ throw new IllegalArgumentException(
+ s"Unable to split argument info path/contents! \n$els",
+ )
+ }
+ }
+ .toMap
+ } else {
+ Map.empty
+ }
+ }
+
+ def fromString(
+ layout: String,
+ root: AbsolutePath = AbsolutePath(Files.createTempDirectory("tapiro")),
+ charset: Charset = StandardCharsets.UTF_8,
+ ): AbsolutePath = {
+ if (!layout.trim.isEmpty) {
+ mapFromString(layout).foreach {
+ case (path, contents) =>
+ val file =
+ path.split("/").foldLeft(root)(_.resolve(_))
+ val parent = file.toNIO.getParent
+ if (!Files.exists(parent)) { // cannot create directories when parent is a symlink
+ Files.createDirectories(parent)
+ }
+ Files.deleteIfExists(file.toNIO)
+ Files.write(
+ file.toNIO,
+ contents.getBytes(charset),
+ StandardOpenOption.WRITE,
+ StandardOpenOption.CREATE,
+ )
+ }
+ }
+ root
+ }
+}
diff --git a/tapiro/core/src/test/scala/io/buildo/tapiro/TapiroSuite.scala b/tapiro/core/src/test/scala/io/buildo/tapiro/TapiroSuite.scala
new file mode 100644
index 00000000..d4b976f7
--- /dev/null
+++ b/tapiro/core/src/test/scala/io/buildo/tapiro/TapiroSuite.scala
@@ -0,0 +1,135 @@
+package io.buildo.tapiro
+
+import java.nio.file.Files
+
+class TapiroSuite extends munit.FunSuite {
+
+ check(
+ "http4s",
+ Server.Http4s,
+ "src/main/scala/schools/endpoints",
+ """
+ |/src/main/scala/schools/SchoolController.scala
+ |package schools
+ |
+ |case class School(id: Long, name: String)
+ |
+ |sealed trait SchoolReadError
+ |object SchoolReadError {
+ | case object NotFound extends SchoolReadError
+ |}
+ |
+ |trait SchoolController[F[_], T] {
+ | @query
+ | def read(id: Long): F[Either[SchoolReadError, School]]
+ |}
+ |""".stripMargin,
+ """
+ |/src/main/scala/schools/endpoints/SchoolControllerTapirEndpoints.scala
+ |//----------------------------------------------------------
+ |// This code was generated by tapiro.
+ |// Changes to this file may cause incorrect behavior
+ |// and will be lost if the code is regenerated.
+ |//----------------------------------------------------------
+ |
+ |package endpoints
+ |import schools._
+ |import sttp.tapir._
+ |import sttp.tapir.Codec.{JsonCodec, PlainCodec}
+ |import sttp.model.StatusCode
+ |
+ |trait SchoolControllerTapirEndpoints[AuthToken] {
+ | val read: Endpoint[Long, SchoolReadError, School, Nothing]
+ |}
+ |
+ |object SchoolControllerTapirEndpoints {
+ |
+ | def create[AuthToken](statusCodes: String => StatusCode)(
+ | implicit codec0: JsonCodec[School],
+ | codec1: JsonCodec[SchoolReadError.NotFound.type],
+ | codec2: PlainCodec[Long]
+ | ) = new SchoolControllerTapirEndpoints[AuthToken] {
+ | override val read: Endpoint[Long, SchoolReadError, School, Nothing] =
+ | endpoint.get
+ | .in("read")
+ | .in(query[Long]("id"))
+ | .errorOut(
+ | oneOf[SchoolReadError](
+ | statusMapping(
+ | statusCodes("NotFound"),
+ | jsonBody[SchoolReadError.NotFound.type]
+ | )
+ | )
+ | )
+ | .out(jsonBody[School])
+ | }
+ |}
+ |
+ |/src/main/scala/schools/endpoints/SchoolControllerHttpEndpoints.scala
+ |//----------------------------------------------------------
+ |// This code was generated by tapiro.
+ |// Changes to this file may cause incorrect behavior
+ |// and will be lost if the code is regenerated.
+ |//----------------------------------------------------------
+ |
+ |package endpoints
+ |import schools._
+ |import cats.effect._
+ |import cats.implicits._
+ |import cats.data.NonEmptyList
+ |import org.http4s._
+ |import org.http4s.server.Router
+ |import sttp.tapir.server.http4s._
+ |import sttp.tapir.Codec.{JsonCodec, PlainCodec}
+ |import sttp.model.StatusCode
+ |
+ |object SchoolControllerHttpEndpoints {
+ |
+ | def routes[F[_]: Sync, AuthToken](
+ | controller: SchoolController[F, AuthToken],
+ | statusCodes: String => StatusCode = _ => StatusCode.UnprocessableEntity
+ | )(
+ | implicit codec0: JsonCodec[School],
+ | codec1: JsonCodec[SchoolReadError.NotFound.type],
+ | codec2: PlainCodec[Long],
+ | cs: ContextShift[F]
+ | ): HttpRoutes[F] = {
+ | val endpoints =
+ | SchoolControllerTapirEndpoints.create[AuthToken](statusCodes)
+ | val read = endpoints.read.toRoutes(controller.read)
+ | Router("/SchoolController" -> NonEmptyList(read, List()).reduceK)
+ | }
+ |}
+ |""".stripMargin,
+ )
+
+ def check(
+ name: String,
+ server: Server,
+ endpointDirectory: String,
+ layout: String,
+ expectedLayout: String,
+ `package`: List[String] = List("endpoints"),
+ )(
+ implicit loc: munit.Location,
+ ): Unit =
+ test(name) {
+ val projectRoot = FileLayout.fromString(layout)
+ val tapiro = new Util()
+ tapiro.createFiles(
+ List(projectRoot.toString),
+ List(projectRoot.toString),
+ projectRoot.resolve(endpointDirectory).toString,
+ `package`,
+ server,
+ )
+
+ FileLayout.mapFromString(expectedLayout).map {
+ case (path, content) =>
+ val filePath = path.split("/").foldLeft(projectRoot)(_.resolve(_))
+ assertNoDiff(Files.readString(filePath.toNIO), content)
+ }
+
+ }
+
+}
diff --git a/tapiro/sbt-tapiro/src/main/scala/io/buildo/tapiro/SbtTapiro.scala b/tapiro/sbt-tapiro/src/main/scala/io/buildo/tapiro/SbtTapiro.scala
index c020b180..a65c0cf8 100644
--- a/tapiro/sbt-tapiro/src/main/scala/io/buildo/tapiro/SbtTapiro.scala
+++ b/tapiro/sbt-tapiro/src/main/scala/io/buildo/tapiro/SbtTapiro.scala
@@ -31,7 +31,7 @@ object SbtTapiro extends AutoPlugin {
override val projectSettings = inConfig(Compile)(
Seq(
tapiro := {
- (new Util(streams.value.log)).createFiles(
+ new Util().createFiles(
tapiroRoutesPaths.in(tapiro).value.map(s => (scalaSource.value / s).toString),
tapiroModelsPaths.in(tapiro).value.map(s => (scalaSource.value / s).toString),
(scalaSource.value / tapiroOutputPath.in(tapiro).value).toString,
diff --git a/tapiro/sbt-tapiro/src/sbt-test/sbt-tapiro/simple/build.sbt b/tapiro/sbt-tapiro/src/sbt-test/sbt-tapiro/simple/build.sbt
index 6b2a0c85..3fde7772 100644
--- a/tapiro/sbt-tapiro/src/sbt-test/sbt-tapiro/simple/build.sbt
+++ b/tapiro/sbt-tapiro/src/sbt-test/sbt-tapiro/simple/build.sbt
@@ -16,7 +16,7 @@ lazy val root = (project in file("."))
"com.softwaremill.sttp.tapir" %% "tapir-core" % "0.12.15",
"org.http4s" %% "http4s-blaze-server" % http4sVersion,
"org.http4s" %% "http4s-circe" % http4sVersion,
- "ch.qos.logback" % "logback-classic" % "1.2.3",
+ "org.apache.logging.log4j" % "log4j-core" % "2.13.1",
) ++ Seq(
"io.circe" %% "circe-core",
"io.circe" %% "circe-generic",
diff --git a/tapiro/sbt-tapiro/src/sbt-test/sbt-tapiro/simple/src/main/resources/logback.xml b/tapiro/sbt-tapiro/src/sbt-test/sbt-tapiro/simple/src/main/resources/logback.xml
deleted file mode 100644
index d0cec6d3..00000000
--- a/tapiro/sbt-tapiro/src/sbt-test/sbt-tapiro/simple/src/main/resources/logback.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-