Quick Start

This documentation needs to be written. You can help us by contributing to the documentation.

Installation

This library is currently available for Scala binary version 3.3.1.

To use the latest version, include the following in your build.sbt:

libraryDependencies ++= Seq(
  "com.rlemaitre" %% "pillars-core" % "0.3.9"
)

You can also add optional modules to your dependencies:

libraryDependencies ++= Seq(
  "com.rlemaitre" %% "pillars-db" % "0.3.9",
  "com.rlemaitre" %% "pillars-db-migration" % "0.3.9",
  "com.rlemaitre" %% "pillars-flags" % "0.3.9",
  "com.rlemaitre" %% "pillars-http-client" % "0.3.9"
)

Usage

You can find an example project in the modules/example directory.

First, you need to create a configuration file. You can find an example in the modules/example/src/main/resources/application.conf file.

Then, you can create your entry point by extending the EntryPoint trait:

object app extends pillars.EntryPoint: // (1)
    def app: pillars.App[IO] = new: // (2)
        def infos: AppInfo = BuildInfo.toAppInfo // (3)

        def run: Run[IO, IO[Unit]] = // (4)
            for
                _ <- logger.info(s"📚 Welcome to ${config.name}!")
                _ <- dbMigration.migrate("classpath:db-migrations") // (5)
                _ <- flag"feature-1".whenEnabled:
                         sessions.use: session =>
                             for
                                 date <- session.unique(sql"select now()".query(timestamptz))
                                 _    <- logger.info(s"The current date is $date.")
                             yield ()
                _ <- http.get("https://swapi.dev/api/people/1"): response =>
                         for
                             _    <- logger.info(s"Response: ${response.status}")
                             size <- response.body.compile.count
                             _    <- logger.info(s"Body: $size bytes")
                         yield ()
                _ <- server.start(homeController, userController)   // (6)
            yield ()
            end for
        end run
end app
1 The EntryPoint trait is a simple trait that provides a main method and initialize the Pillars instance.
2 The pillars.App[IO] must contain your application logic
3 infos defines some metadata about your application. It is used by the admin server to display information about your application. See
4 The run is the entry point of your application. Here, you have access to the Pillars instance.

Then, you can run your application. For example, you can run it with sbt:

sbt "example/run"

The log should display something like:

2024.01.21 22:36:19:0000 [io-comp...] [INFO ] pillars.Pillars.apply:52 - Loading modules...
2024.01.21 22:36:19:0001 [io-comp...] [INFO ] pillars.Pillars.loadModules:87 - Found 2 module loaders: db, feature-flags
2024.01.21 22:36:19:0002 [io-comp...] [INFO ] pillars.db.db.load:57 - Loading DB module
2024.01.21 22:36:19:0003 [io-comp...] [INFO ] pillars.db.db.load:68 - DB module loaded
2024.01.21 22:36:19:0004 [io-comp...] [INFO ] pillars.flags.FlagManager.load:54 - Loading Feature flags module
2024.01.21 22:36:19:0005 [io-comp...] [INFO ] pillars.flags.FlagManager.load:57 - Feature flags module loaded
2024.01.21 22:36:19:0000 [io-comp...] [INFO ] pillars.AdminServer.start:22 - Starting admin server on 0.0.0.0:19876
2024.01.21 22:36:19:0006 [io-comp...] [INFO ] example.app.run:24 - 📚 Welcome to Bookstore!
2024.01.21 22:36:19:0000 [io-comp...] [INFO ] example.app.run:29 - The current date is 2024-01-21T22:36:19.695572+01:00.
2024.01.21 22:36:19:0000 [io-comp...] [INFO ] pillars.ApiServer.init:21 - Starting API server on 0.0.0.0:9876
2024.01.21 22:36:19:0001 [io-comp...] [INFO ] org.http4s.netty.server.NettyServerBuilder - Using NIO EventLoopGroup
2024.01.21 22:36:19:0001 [io-comp...] [INFO ] org.http4s.netty.server.NettyServerBuilder - Using NIO EventLoopGroup
2024.01.21 22:36:19:0002 [io-comp...] [INFO ] org.http4s.netty.server.NettyServerBuilder - Started Http4s Netty Server at http://[::]:9876/
2024.01.21 22:36:19:0002 [io-comp...] [INFO ] org.http4s.netty.server.NettyServerBuilder - Started Http4s Netty Server at http://[::]:19876/

You can now access the API at http://localhost:9876 and the admin server at http://localhost:19876. For example, to get the readiness porbe status, you can run:

$ curl http://localhost:19876/admin/probes/health | jq
{
  "status": "pass",
  "checks": [
    {
      "componentId": "db",
      "componentType": "datastore",
      "status": "pass"
    }
  ]
}

Application Metadata

The infos property of the App[F] trait defines some metadata about your application. You have two ways of defining it:

You can directly create an instance of AppInfo:

val infos = AppInfo(
  name = App.Name("Bookstore"),
  version = App.Version("1.0.0"),
  description = App.Description("A simple bookstore")
)

Or, if you are using the sbt-buildinfo plugin, you can use the BuildInfo object. In your build.sbt, add the following lines to your project definition:

lazy val example = Project("pillars-example", file("modules/example"))
    .enablePlugins(BuildInfoPlugin) // (1)
    .settings(
      name                                        := "pillars-example", // (2)
      description                                 := "pillars-example is an example of application using pillars", // (3)
      libraryDependencies ++= Dependencies.tests ++ Dependencies.migrationsRuntime,
      buildInfoKeys                               := Seq[BuildInfoKey](name, version, description), // (4)
      buildInfoOptions                            := Seq(BuildInfoOption.Traits("pillars.BuildInfo")), // (5)
      buildInfoPackage                            := "example.build", // (6)
      publish / skip                              := true,
      libraryDependencySchemes += "org.typelevel" %% "otel4s-core-trace" % VersionScheme.Always
    )
    .dependsOn(core, dbSkunk, flags, httpClient, dbMigrations)
1 Enable the BuildInfo plugin
2 Define the name of your application
3 Define the description of your application
4 Tell buildinfo to generate the BuildInfo object including at least name, description and version properties. In this specific case, version is defined by the sbt-dynver plugin.
5 Configure BuildInfo to implement the pillars.BuildInfo trait. It is required to use the BuildInfo object in your application.
6 Specify in which package will be generated the BuildInfo object.

Then, you can use the BuildInfo object in your application:

import example.build.BuildInfo

val app = new App[IO]:
  override val infos = BuildInfo.toAppInfo
  override def run(pillars: Pillars[IO]): IO[Unit] = ???