Skip to content

Commit

Permalink
refactor(core): Use context functions (#59)
Browse files Browse the repository at this point in the history
  • Loading branch information
rlemaitre authored Feb 16, 2024
1 parent 00e236d commit 2483dac
Show file tree
Hide file tree
Showing 11 changed files with 78 additions and 21 deletions.
1 change: 1 addition & 0 deletions modules/core/src/main/scala/pillars/ApiServer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ trait ApiServer[F[_]]:
def start(endpoints: List[HttpEndpoint[F]]): F[Unit]

object ApiServer:
def apply[F[_]]: Run[F, ApiServer[F]] = summon[Pillars[F]].apiServer
def init[F[_]: Async](config: Config, observability: Observability[F], logger: Scribe[F]): ApiServer[F] =
(endpoints: List[HttpEndpoint[F]]) =>
Async[F].whenA(config.enabled):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import scala.io.Source
import scala.util.matching.Regex
import scodec.bits.ByteVector

object config:
object Config:
def apply[F[_]]: Run[F, PillarsConfig] = summon[Pillars[F]].config
case class PillarsConfig(
name: App.Name,
log: Logging.Config = Logging.Config(),
Expand Down Expand Up @@ -98,4 +99,4 @@ object config:
case ConfigError.MissingEnvironmentVariable(name) => Message(s"Missing environment variable $name".assume)
case ConfigError.ParsingError(cause) => Message(s"Failed to parse configuration: ${cause.getMessage}".assume)
end ConfigError
end config
end Config
6 changes: 5 additions & 1 deletion modules/core/src/main/scala/pillars/Logging.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,21 @@ import io.github.iltotore.iron.constraint.all.*
import java.nio.file.Path
import scribe.Level
import scribe.Logger
import scribe.Scribe
import scribe.file.PathBuilder
import scribe.format.Formatter
import scribe.json.ScribeCirceJsonSupport
import scribe.writer.ConsoleWriter
import scribe.writer.Writer

object Logger:
def apply[F[_]: Pillars]: Run[F, Scribe[F]] = summon[Pillars[F]].logger

object Logging:
def init[F[_]: Sync](config: Config): F[Unit] =
Sync[F]
.delay(
Logger.root
scribe.Logger.root
.clearHandlers()
.clearModifiers()
.withHandler(
Expand Down
33 changes: 31 additions & 2 deletions modules/core/src/main/scala/pillars/Pillars.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import io.circe.Decoder
import java.nio.file.Path
import java.util.ServiceLoader
import org.typelevel.otel4s.trace.Tracer
import pillars.config.PillarsConfig
import pillars.config.Reader
import pillars.Config.PillarsConfig
import pillars.Config.Reader
import pillars.probes.ProbeManager
import pillars.probes.ProbesController
import scala.jdk.CollectionConverters.IterableHasAsScala
Expand All @@ -20,11 +20,40 @@ import scribe.*
* The Pillars trait defines the main components of the application.
*/
trait Pillars[F[_]]:
/**
* Component for observability. It allows you to create spans and metrics.
*/
def observability: Observability[F]

/**
* The configuration for the application.
*/
def config: PillarsConfig

/**
* The API server for the application.
*
* It has to be manually started by calling the `start` method in the application.
*/
def apiServer: ApiServer[F]

/**
* The logger for the application.
*/
def logger: Scribe[F]

/**
* Reads a configuration from the configuration.
*
* @return the configuration.
*/
def readConfig[T](using Decoder[T]): F[T]

/**
* Gets a module from the application.
*
* @return the module.
*/
def module[T <: Module[F]: ClassTag]: T
end Pillars

Expand Down
10 changes: 4 additions & 6 deletions modules/core/src/main/scala/pillars/app.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ trait App[F[_]]:
def infos: AppInfo
def probes: List[Probe[F]] = Nil
def adminControllers: List[Controller[F]] = Nil
def run(using p: Pillars[F]): F[Unit]
def run: Run[F, F[Unit]]
end App

object App:
Expand Down Expand Up @@ -50,12 +50,10 @@ trait EntryPoint extends IOApp:
override final def run(args: List[String]): IO[ExitCode] =
Command(app.infos.name, app.infos.description)(
Opts.option[Path]("config", "Path to the configuration file")
).parse(
args,
sys.env
) match
).parse(args, sys.env) match
case Left(help) => Console[IO].errorln(help).as(ExitCode.Error)
case Right(configPath) =>
Pillars(configPath).use: pillars =>
app.run(using pillars).as(ExitCode.Success)
given Pillars[IO] = pillars
app.run.as(ExitCode.Success)
end EntryPoint
2 changes: 1 addition & 1 deletion modules/core/src/main/scala/pillars/modules.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import cats.effect.Resource
import cats.effect.std.Console
import fs2.io.net.Network
import org.typelevel.otel4s.trace.Tracer
import pillars.config.Reader
import pillars.Config.Reader
import pillars.probes.Probe
import scala.reflect.ClassTag
import scribe.Scribe
Expand Down
8 changes: 8 additions & 0 deletions modules/core/src/main/scala/pillars/package.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
package object pillars:
/**
* Type alias for a Pillars[F] context bound.
*
* @tparam F The effect type.
* @tparam A The type of the value that is being computed.
*/
type Run[F[_], A] = Pillars[F] ?=> A

extension [T](items: Iterable[T])
/**
* Extension method for Iterable[T] to perform topological sorting.
Expand Down
2 changes: 1 addition & 1 deletion modules/db/src/main/scala/pillars/db/db.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import io.github.iltotore.iron.*
import io.github.iltotore.iron.circe.given
import io.github.iltotore.iron.constraint.all.*
import org.typelevel.otel4s.trace.Tracer
import pillars.Config.*
import pillars.Loader
import pillars.Module
import pillars.Modules
import pillars.Pillars
import pillars.codec.given
import pillars.config.*
import pillars.probes.*
import skunk.*
import skunk.codec.all.*
Expand Down
17 changes: 17 additions & 0 deletions modules/example/src/main/rest/admin.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@host = http://localhost:19876
###
# @name: List all flags
#
GET {{ host }}/admin/flags/

###
# @name: Get flag by id

@featureId = feature-1

GET {{ host }}/admin/flags/{{featureId}}

###
#

GET {{ host }}/admin/probes/health
13 changes: 6 additions & 7 deletions modules/example/src/main/scala/example/app.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,21 @@ import skunk.implicits.*

// tag::quick-start[]
object app extends pillars.EntryPoint: // // <1>
def app: pillars.App[IO] = new pillars.App[IO]: // // <2>
def app: pillars.App[IO] = new: // // <2>
def infos: AppInfo = BuildInfo.toAppInfo // // <3>

def run(using p: Pillars[IO]): IO[Unit] = // // <4>
import p.*
def run: Run[IO, IO[Unit]] = // // <4>
for
_ <- logger.info(s"📚 Welcome to ${config.name}!")
_ <- Logger[IO].info(s"📚 Welcome to ${Config[IO].name}!")
_ <- flag"feature-1".whenEnabled:
DB[IO].use: session =>
for
date <- session.unique(sql"select now()".query(timestamptz))
_ <- logger.info(s"The current date is $date.")
_ <- Logger[IO].info(s"The current date is $date.")
yield ()
_ <- HttpClient[IO].get("https://pillars.rlemaitre.com"): response =>
logger.info(s"Response: ${response.status}")
_ <- apiServer.start(endpoints.all)
Logger[IO].info(s"Response: ${response.status}")
_ <- ApiServer[IO].start(endpoints.all)
yield ()
end for
end run
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ final case class HttpClient[F[_]: Async](client: org.http4s.client.Client[F])

object HttpClient:
def apply[F[_]](using p: Pillars[F]): Client[F] = p.module[HttpClient[F]].client
final case class Config(followRedirect: Boolean)
private[httpclient] final case class Config(followRedirect: Boolean)
extension [F[_]](p: Pillars[F])
def httpClient: Client[F] = p.module[HttpClient[F]].client

0 comments on commit 2483dac

Please sign in to comment.