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

0.4.0 release #6

Merged
merged 9 commits into from
Oct 14, 2024
Merged
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
15 changes: 6 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Install sbt
if: contains(runner.os, 'macos')
run: brew install sbt
uses: sbt/setup-sbt@v1

- name: Checkout current branch (full)
uses: actions/checkout@v4
Expand Down Expand Up @@ -98,11 +97,11 @@ jobs:

- name: Make target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/master')
run: mkdir -p testkit/.native/target testkit/.js/target core/.native/target core/.js/target core/.jvm/target testkit/.jvm/target project/target
run: mkdir -p core/.native/target core/.js/target core/.jvm/target testkit/.jvm/target project/target

- name: Compress target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/master')
run: tar cf targets.tar testkit/.native/target testkit/.js/target core/.native/target core/.js/target core/.jvm/target testkit/.jvm/target project/target
run: tar cf targets.tar core/.native/target core/.js/target core/.jvm/target testkit/.jvm/target project/target

- name: Upload target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/master')
Expand All @@ -122,8 +121,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Install sbt
if: contains(runner.os, 'macos')
run: brew install sbt
uses: sbt/setup-sbt@v1

- name: Checkout current branch (full)
uses: actions/checkout@v4
Expand Down Expand Up @@ -242,16 +240,15 @@ jobs:

dependency-submission:
name: Submit Dependencies
if: github.event_name != 'pull_request'
if: github.event.repository.fork == false && github.event_name != 'pull_request'
strategy:
matrix:
os: [ubuntu-latest]
java: [temurin@11]
runs-on: ${{ matrix.os }}
steps:
- name: Install sbt
if: contains(runner.os, 'macos')
run: brew install sbt
uses: sbt/setup-sbt@v1

- name: Checkout current branch (full)
uses: actions/checkout@v4
Expand Down
87 changes: 48 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ http4s-stir also furnishes a test kit akin to Pekko's (Akka's).
In SBT:

```scala
libraryDependencies += "pl.iterators" %% "http4s-stir" % "0.2"
libraryDependencies += "pl.iterators" %% "http4s-stir-testkit" % "0.2" % Test // if you need this
libraryDependencies += "pl.iterators" %% "http4s-stir" % "0.4.0"
libraryDependencies += "pl.iterators" %% "http4s-stir-testkit" % "0.4.0" % Test // if you need this
```

For `scala-cli` see [this example](#example).
Expand All @@ -25,15 +25,15 @@ For `scala-cli` see [this example](#example).

Here's an example in Scala 3 that you can run using scala-cli:

```scala
```scala 3
// Main.scala
//> using dep org.typelevel::cats-effect:3.5.1
//> using dep org.http4s::http4s-dsl:0.23.23
//> using dep org.http4s::http4s-ember-server:0.23.23
//> using dep org.http4s::http4s-circe:0.23.23
//> using dep io.circe::circe-core:0.14.5
//> using dep io.circe::circe-generic:0.14.5
//> using dep pl.iterators::http4s-stir:0.2
//> using dep org.typelevel::cats-effect::3.5.4
//> using dep org.http4s::http4s-dsl::0.23.28
//> using dep org.http4s::http4s-ember-server::0.23.28
//> using dep org.http4s::http4s-circe::0.23.28
//> using dep io.circe::circe-core::0.14.10
//> using dep io.circe::circe-generic::0.14.10
//> using dep pl.iterators::http4s-stir::0.4.0

import org.http4s.Status
import org.http4s.ember.server.EmberServerBuilder
Expand Down Expand Up @@ -94,17 +94,23 @@ val route: Route =

object Main extends IOApp.Simple {
val run = EmberServerBuilder
.default[IO]
.withHttpApp(route.toHttpRoutes.orNotFound)
.build
.use(_ => IO.never)
.default[IO]
.withHttpApp(route.toHttpRoutes.orNotFound)
.build
.use(_ => IO.never)
}

```

To run this service you can use `scala-cli run .`.

Or maybe if you want, you can compile it to JS file: `scala-cli --power package --js --js-module-kind commonjs Main.scala`.

```scala 3
// Main.test.scala
//> using test.dep org.specs2::specs2-core:4.19.2
//> using test.dep pl.iterators::http4s-stir-testkit:0.2
//> using test.dep org.specs2::specs2-core:5.5.8
//> using test.dep pl.iterators::http4s-stir-testkit:0.4.0
//> using test.dep org.http4s::http4s-circe:0.23.28

import org.http4s.Status
import org.http4s.circe.CirceEntityEncoder.*
Expand All @@ -115,33 +121,36 @@ import org.specs2.mutable.Specification
import pl.iterators.stir.testkit.Specs2RouteTest

class MainRoutesSpec extends Specification with Specs2RouteTest {
override implicit val runtime: IORuntime = IORuntime.global

sequential
"The routes" should {
"create order" in {
Post("/create-order", Order(List(Item("foo", 42)))) ~> route ~> check {
responseAs[String] must contain("order created")
orders.head must beEqualTo(Item("foo", 42))
}
}
"retrieve an item if present" in {
orders = List(Item("foo", 42))
Get("/item/42") ~> route ~> check {
responseAs[Item] must beEqualTo(Item("foo", 42))
}
}
"return 404 if item is not present" in {
orders = List.empty
Get("/item/42") ~> route ~> check {
status must beEqualTo(Status.NotFound)
}
}
override implicit val runtime: IORuntime = IORuntime.global

sequential
"The routes" should {
"create order" in {
Post("/create-order", Order(List(Item("foo", 42)))) ~> route ~> check {
responseAs[String] must contain("order created")
orders.head must beEqualTo(Item("foo", 42))
}
}
"retrieve an item if present" in {
orders = List(Item("foo", 42))
Get("/item/42") ~> route ~> check {
responseAs[Item] must beEqualTo(Item("foo", 42))
}
}
"return 404 if item is not present" in {
orders = List.empty
Get("/item/42") ~> route ~> check {
status must beEqualTo(Status.NotFound)
}
}
}
}

```

For a more comprehensive example showcasing additional directives see [examples](https://github.com/theiterators/http4s-stir/blob/master/examples/src/main/scala/Service.scala).
To run the tests you can use `scala-cli test .`.

For a more comprehensive example showcasing additional directives see [examples](https://github.com/theiterators/http4s-stir/blob/master/examples/src/main/scala/Service.scala). You can run it with `~examples/reStart`.

## Why this library?

Expand Down
76 changes: 45 additions & 31 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ val supportedScalaVersions = Seq(scala_2_13, scala_3)
ThisBuild / crossScalaVersions := supportedScalaVersions
ThisBuild / scalaVersion := mainScalaVersion

ThisBuild / versionScheme := Some("early-semver")
ThisBuild / githubWorkflowJavaVersions := Seq(JavaSpec.temurin("11"), JavaSpec.temurin("17"))
ThisBuild / githubWorkflowPublishTargetBranches := Seq(RefPredicate.StartsWith(Ref.Tag("v")),
RefPredicate.Equals(Ref.Branch("master")))
ThisBuild / tlBaseVersion := "0.3"
ThisBuild / tlBaseVersion := "0.4"
ThisBuild / tlCiHeaderCheck := false
ThisBuild / tlSonatypeUseLegacyHost := true
ThisBuild / sonatypeCredentialHost := xerial.sbt.Sonatype.sonatypeLegacy

lazy val noPublishSettings =
Seq(
Expand Down Expand Up @@ -53,65 +54,78 @@ lazy val baseSettings = Seq(
crossScalaVersions := supportedScalaVersions,
scalafmtOnCompile := true)

val http4s = Seq(
"org.http4s" %% "http4s-dsl" % "0.23.28",
"org.http4s" %% "http4s-ember-server" % "0.23.28")
val http4sDsl = Def.setting("org.http4s" %%% "http4s-dsl" % "0.23.28")
val http4sEmber = Def.setting("org.http4s" %%% "http4s-ember-server" % "0.23.28")

val http4sClient = Seq(
"org.http4s" %% "http4s-ember-client" % "0.23.28")
val fs2Core = Def.setting("co.fs2" %%% "fs2-core" % "3.11.0")
val fs2Io = Def.setting("co.fs2" %%% "fs2-io" % "3.11.0")

val circe = Seq(
"io.circe" %% "circe-core" % "0.14.10",
"io.circe" %% "circe-generic" % "0.14.10",
"io.circe" %% "circe-parser" % "0.14.10",
"org.http4s" %% "http4s-circe" % "0.23.28")
val http4sClient = Def.setting(
"org.http4s" %%% "http4s-ember-client" % "0.23.28")

val logback = Seq(
"ch.qos.logback" % "logback-classic" % "1.5.8")
val circeCore = Def.setting("io.circe" %%% "circe-core" % "0.14.8")
val circeGeneric = Def.setting("io.circe" %%% "circe-generic" % "0.14.10")
val circeParser = Def.setting("io.circe" %%% "circe-parser" % "0.14.10")
val http4sCirce = Def.setting("org.http4s" %%% "http4s-circe" % "0.23.28")

lazy val core = crossProject(JSPlatform, JVMPlatform, NativePlatform)
val scalatest = Def.setting("org.scalatest" %%% "scalatest" % "3.2.19")
val specs2 = Def.setting("org.specs2" %%% "specs2-core" % "4.20.6")

val scalaXml = Def.setting("org.scala-lang.modules" %%% "scala-xml" % "2.2.0")

lazy val core = crossProject(JVMPlatform, NativePlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Pure)
.in(file("core"))
.settings(baseSettings *)
.settings(
name := "http4s-stir",
libraryDependencies ++= http4s,
libraryDependencies ++= Seq(http4sDsl.value, http4sEmber.value) ++ Seq(fs2Core.value,
fs2Io.value) ++ Seq(scalaXml.value),
Compile / doc / scalacOptions -= "-Xfatal-warnings")

lazy val coreTests = project
lazy val coreTests = crossProject(JVMPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Pure)
.in(file("core-tests"))
.settings(baseSettings *)
.settings(noPublishSettings *)
.settings(
name := "http4s-stir-tests",
libraryDependencies ++= http4s ++ circe ++ Seq(
"org.scalatest" %% "scalatest" % "3.2.19" % Test,
"org.specs2" %% "specs2-core" % "4.20.8" % Test)).dependsOn(
testkit.jvm % "test",
core.jvm % "test->test")
libraryDependencies ++= Seq(http4sDsl.value, http4sEmber.value) ++
Seq(circeCore.value, circeGeneric.value, circeParser.value, http4sCirce.value) ++
Seq(
scalatest.value % Test,
specs2.value % Test))
.dependsOn(
testkit % "test",
core % "test->test")

lazy val testkit = crossProject(JSPlatform, JVMPlatform, NativePlatform)
lazy val testkit = crossProject(JVMPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Pure)
.in(file("testkit"))
.settings(baseSettings *)
.settings(
name := "http4s-stir-testkit",
libraryDependencies ++= http4s ++ http4sClient ++ Seq(
"org.scalatest" %% "scalatest" % "3.2.19" % "provided",
"org.specs2" %% "specs2-core" % "4.20.8" % "provided")).dependsOn(core)
libraryDependencies ++= Seq(http4sClient.value) ++ Seq(
scalatest.value % "provided",
specs2.value % "provided"))
.dependsOn(core)

lazy val examples = project
lazy val examples = crossProject(JVMPlatform)
.withoutSuffixFor(JVMPlatform)
.crossType(CrossType.Pure)
.in(file("examples"))
.settings(baseSettings *)
.settings(noPublishSettings *)
.settings(
name := "http4s-stir-examples",
libraryDependencies ++= http4s ++ circe ++ logback ++ Seq(
"org.specs2" %% "specs2-core" % "4.20.8" % Test,
"org.scalatest" %% "scalatest" % "3.2.19" % Test))
.dependsOn(core.jvm, testkit.jvm % Test)
libraryDependencies ++= Seq(http4sDsl.value, http4sEmber.value) ++ Seq(circeCore.value, circeGeneric.value,
circeParser.value, http4sCirce.value) ++ Seq(
specs2.value % Test,
scalatest.value % Test))
.dependsOn(core, testkit % Test)

lazy val root = tlCrossRootProject.aggregate(core, testkit, examples, coreTests)
.settings(baseSettings *)
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class DebuggingDirectivesSpec extends RoutingSpec {
resetDebugMsg()
Get("/hello") ~> route ~> check {
response.status shouldEqual Status.Ok
normalizedDebugMsg() shouldEqual "HTTP/1.1 GET /hello Headers() body=\"\"\n"
normalizedDebugMsg() shouldEqual "HTTP/1.1 GET /hello Headers()\n"
}
}
}
Expand All @@ -54,7 +54,7 @@ class DebuggingDirectivesSpec extends RoutingSpec {
resetDebugMsg()
Get("/hello") ~> route ~> check {
response.status shouldEqual Status.Ok
normalizedDebugMsg() shouldEqual "HTTP/1.1 200 OK Headers() body=\"\"\n"
normalizedDebugMsg() shouldEqual "HTTP/1.1 200 OK Headers()\n"
}
}
}
Expand All @@ -69,9 +69,9 @@ class DebuggingDirectivesSpec extends RoutingSpec {
Get("/hello") ~> route ~> check {
response.status shouldEqual Status.Ok
normalizedDebugMsg() shouldEqual
"""|HTTP/1.1 GET /hello Headers() body=""
|HTTP/1.1 200 OK Headers() body=""
|""".stripMarginWithNewline("\n")
"""|HTTP/1.1 GET /hello Headers()
|HTTP/1.1 200 OK Headers()
|""".stripMarginWithNewline("\n")
}
}
// "be able to log only rejections" in {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package pl.iterators.stir.server

import cats.effect.IO
import cats.effect.std.Console
import org.http4s.Status.InternalServerError
import org.typelevel.log4cats

import scala.util.control.NonFatal

Expand Down Expand Up @@ -43,7 +43,7 @@ object ExceptionHandler {
*/
def default(logAction: Option[(Throwable, String) => IO[Unit]] = None): ExceptionHandler = {
val log = logAction.getOrElse { (t: Throwable, s: String) =>
log4cats.slf4j.Slf4jFactory.create[IO].getLogger.error(t)(s)
Console[IO].errorln(s) *> Console[IO].printStackTrace(t)
}
apply(knownToBeSealed = true) {
case NonFatal(e) => ctx => {
Expand Down
Loading