Added odin logging + store middleware

This commit is contained in:
Rohan Sircar 2020-12-30 22:47:29 +05:30
parent f294ab7009
commit d455175044
5 changed files with 68 additions and 20 deletions

View File

@ -33,7 +33,9 @@ libraryDependencies ++= Seq(
// "io.circe" %%% "circe-config" % "0.8.0", // "io.circe" %%% "circe-config" % "0.8.0",
"org.akka-js" %%% "shocon" % "1.0.0", "org.akka-js" %%% "shocon" % "1.0.0",
"com.beachape" %%% "enumeratum-circe" % "1.6.1", "com.beachape" %%% "enumeratum-circe" % "1.6.1",
"com.github.valskalla" %%% "odin-core" % "0.7.0+95-ab4381ae+20201227-1831-SNAPSHOT" "com.github.valskalla" %%% "odin-core" % "0.7.0+95-ab4381ae+20201227-1831-SNAPSHOT",
"io.github.cquiroz" %%% "scala-java-time" % "2.1.0",
"io.github.cquiroz" %%% "scala-java-time-tzdb" % "2.1.0"
// "com.clovellytech" %%% "outwatch-router" % "0.0.9+7-5be0b1a2+20201227-2019-SNAPSHOT" // "com.clovellytech" %%% "outwatch-router" % "0.0.9+7-5be0b1a2+20201227-2019-SNAPSHOT"
) )

View File

@ -4,10 +4,10 @@ import scala.concurrent.duration._
import colibri.ext.monix._ import colibri.ext.monix._
import com.softwaremill.macwire._ import com.softwaremill.macwire._
import io.odin.consoleLogger
import monix.bio.Task import monix.bio.Task
import monix.eval.Coeval import monix.eval.Coeval
import monix.reactive.Observable import monix.reactive.Observable
import outwatchapp.ui.components.todo.TodoListStore
import org.scalajs.dom.raw.Element import org.scalajs.dom.raw.Element
import outwatch._ import outwatch._
import outwatch.dsl._ import outwatch.dsl._
@ -15,10 +15,11 @@ import outwatch.router._
import outwatchapp.components.CounterDemo import outwatchapp.components.CounterDemo
import outwatchapp.components.RequestDemo import outwatchapp.components.RequestDemo
import outwatchapp.components.todo.ChartjsDemo import outwatchapp.components.todo.ChartjsDemo
import outwatchapp.pages.HomePage
import outwatchapp.util.reactive.WorkerData
import outwatchapp.util.reactive.WebWorker
import outwatchapp.components.todo.FusejsDemo import outwatchapp.components.todo.FusejsDemo
import outwatchapp.pages.HomePage
import outwatchapp.ui.components.todo.TodoListStore
import outwatchapp.util.reactive.WebWorker
import outwatchapp.util.reactive.WorkerData
class MainApp(el: Element)(implicit class MainApp(el: Element)(implicit
backend: AppTypes.Backend, backend: AppTypes.Backend,
@ -34,7 +35,7 @@ class MainApp(el: Element)(implicit
for { for {
counterDemo <- CounterDemo() counterDemo <- CounterDemo()
chartDemo <- ChartjsDemo() chartDemo <- ChartjsDemo()
todoStore <- TodoListStore() todoStore <- TodoListStore(consoleLogger[Task]())
requestDemo <- RequestDemo(todoStore) requestDemo <- RequestDemo(todoStore)
demoWorker <- WebWorker[WorkerData]("/worker.js") demoWorker <- WebWorker[WorkerData]("/worker.js")
} yield { } yield {

View File

@ -14,7 +14,7 @@ import scalajs.js
@JSImport("bootstrap/dist/css/bootstrap.min.css", JSImport.Namespace) @JSImport("bootstrap/dist/css/bootstrap.min.css", JSImport.Namespace)
@js.native @js.native
object BootstrapBundleCss extends js.Object object BootstrapBundleCss extends js.Object
@JSImport("bootstrap", JSImport.Namespace) @JSImport("bootstrap/dist/js/bootstrap.bundle.min.js", JSImport.Namespace)
@js.native @js.native
object BootstrapBundleJs extends js.Object object BootstrapBundleJs extends js.Object

View File

@ -1,15 +1,18 @@
package nova.monadic_sfx.ui.components.todo package outwatchapp.ui.components.todo
import scala.scalajs.js.annotation.JSExportAll import scala.scalajs.js.annotation.JSExportAll
import cats.kernel.Eq import cats.kernel.Eq
import com.softwaremill.quicklens._ import com.softwaremill.quicklens._
import io.circe.generic.JsonCodec import io.circe.generic.JsonCodec
import io.odin.Logger
import monix.bio.Task import monix.bio.Task
import nova.monadic_sfx.util.reactive.store.Reducer import outwatchapp.util.reactive.store.Middlewares.actionLoggerMiddleware
import nova.monadic_sfx.util.reactive.store.Store import outwatchapp.util.reactive.store.Reducer
import outwatchapp.util.reactive.store.Store
@JSExportAll @JSExportAll
@JsonCodec
case class Todo(id: Int, content: String) case class Todo(id: Int, content: String)
object Todo { object Todo {
implicit val eqForTodo = Eq.fromUniversalEquals[Todo] implicit val eqForTodo = Eq.fromUniversalEquals[Todo]
@ -37,18 +40,16 @@ object TodoListStore {
implicit val eqForState = Eq.fromUniversalEquals[State] implicit val eqForState = Eq.fromUniversalEquals[State]
} }
def reducer()( def reducer(logger: Logger[Task])(
state: State, state: State,
action: Action action: Action
): (State, Option[Task[Action]]) = ): (State, Option[Task[Action]]) =
action match { action match {
case Init => (state, None) case Init => (state, None)
case Add(content) => case Add(content) =>
println("hello")
val nextAction = Some(for { val nextAction = Some(for {
// do some validation // do some validation
// _ <- logger.debug(s"Received $content") // _ <- logger.debug(s"Received $content")
_ <- Task(println(s"Received $content"))
res <- Task.pure(InternalAdd(content)) res <- Task.pure(InternalAdd(content))
} yield res) } yield res)
(state, nextAction) (state, nextAction)
@ -68,19 +69,21 @@ object TodoListStore {
.using(_ :+ Todo(state.counter, content)) .using(_ :+ Todo(state.counter, content))
.modify(_.counter) .modify(_.counter)
.using(_ + 1) .using(_ + 1)
(nextState, Some(Task.pure(End))) (nextState, Some(logger.debug(s"Received $content") >> Task.pure(End)))
case End => (state, None) case End => (state, None)
} }
def apply(): Task[Store[Action, State]] = def apply(logger: Logger[Task]): Task[Store[Action, State]] =
Task.deferAction(implicit s => Task.deferAction(implicit s =>
for { for {
logMware <- actionLoggerMiddleware[Action, State](logger, "TodoStore")
store <- store <-
Store Store
.createL[Action, State]( .createL[Action, State](
Init, Init,
State(Vector.empty[Todo], 0), State(Vector.empty[Todo], 0),
Reducer.withOptionalEffects(reducer() _) Reducer.withOptionalEffects(reducer(logger) _),
Seq(logMware)
) )
} yield store } yield store
) )

View File

@ -2,11 +2,17 @@ package outwatchapp.util.reactive.store
import java.time.LocalDateTime import java.time.LocalDateTime
import io.circe.Encoder
import io.circe.Printer import io.circe.Printer
import io.circe.generic.JsonCodec import io.circe.generic.JsonCodec
// object Middleware { import io.circe.syntax._
// def apply[A,M,T](ob: Observable[(A,M)], cb: (A,M) => T): Observable[(A,M)] = ob import io.odin.Logger
// } import io.odin.LoggerMessage
import io.odin.formatter.options.PositionFormat
import io.odin.formatter.options.ThrowableFormat
import io.odin.meta.Render
import monix.bio.Task
import monix.reactive.Observable
@JsonCodec @JsonCodec
final case class StoreInfo[A]( final case class StoreInfo[A](
@ -17,6 +23,42 @@ final case class StoreInfo[A](
object StoreInfo { object StoreInfo {
val printer = Printer.noSpaces val printer = Printer.noSpaces
implicit def render[T: Encoder]: Render[StoreInfo[T]] =
new Render[StoreInfo[T]] {
override def render(m: StoreInfo[T]): String = printer.print(m.asJson)
}
} }
object Middlewares {} object Middlewares {
val format = create(ThrowableFormat.Default, PositionFormat.Full)
def create(
throwableFormat: ThrowableFormat,
positionFormat: PositionFormat
): io.odin.formatter.Formatter = (msg: LoggerMessage) => msg.message.value
def actionStateLoggerMiddleware[A, M](
logger: Logger[Task]
): Task[Middleware[A, M]] =
Task.deferAction(implicit s =>
Task((obs: Observable[(A, M)]) =>
obs.doOnNextF { case (a, m) =>
logger.debug(s"Received action $a with state $m")
}
)
)
def actionLoggerMiddleware[A: Encoder, M](
logger: Logger[Task],
name: String
): Task[Middleware[A, M]] =
Task.deferAction(implicit s =>
Task((obs: Observable[(A, M)]) =>
obs.doOnNextF { case (a, _) =>
Task(LocalDateTime.now())
.flatMap(curTime => logger.debug(StoreInfo(name, a, curTime)))
}
)
)
}