Created
May 31, 2023 11:49
-
-
Save adamretter/190563b6eb09a24848e91bd9b881807e to your computer and use it in GitHub Desktop.
Mock Scala cats-effect Service that runs forever in a CLI App
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package uk.gov.nationalarchives.omega.api | |
import cats.effect.std.Supervisor | |
import cats.effect.{ExitCode, IO, IOApp} | |
import scala.concurrent.duration.DurationInt | |
object MockApp extends IOApp { | |
override def run(args: List[String]): IO[ExitCode] = { | |
// TODO(AR) flip these boolean flags to simulate startup and shutdown failures | |
val service = new MockService(failStart = false, failStop = true) | |
// TODO(AR) we want to: | |
// 1. Start our MockService (i.e. `MockService#start`) and have it run forever. However: | |
// 2. If startup fails we need to report this to the CLI and exit | |
// 3. If SIGINT (e.g. Ctrl-C) is received we should shutdown our MockService (i.e. `MockService#stop`) | |
// 3.1 If shutdown fails we need to report this to the CLI and exit with a non-zero exit code | |
// 3.2 If shutdown succeeds we need to exit OK | |
// NOTE(AR) at the moment only (1) and (3.2) are working, i.e. (failStart = false, failStop = false) | |
Supervisor[IO](await = false) | |
.use { supervisor => | |
for { | |
_ <- supervisor.supervise[Option[ErrorCondition]](service.start) | |
_ <- IO.sleep(5.seconds).foreverM | |
} yield () | |
} | |
.onCancel(IO.println("Called Supervisor Stop...") >> service.stop().as(IO.unit)) // TODO(AR) we need the ErrorCondition to reply to the CLI maybe instead of IO[Unit] | |
.as(ExitCode.Success) | |
} | |
} | |
case class ErrorCondition(msg: String) | |
class MockService(failStart: Boolean, failStop: Boolean) { | |
/** | |
* This function will either return a `Some(ErrorCondition)` relatively quickly if startup fails, | |
* or if startup succeeds it will run (i.e. block) forever! | |
*/ | |
def start: IO[Option[ErrorCondition]] = { | |
// if `failStart` is set return an errorCode, else run forever... | |
IO.println("Start has started...") >> | |
IO.pure(failStart).ifM( | |
IO.pure(Some(ErrorCondition("START FAILED"))), | |
IO.never // Started OK and now running... (Note: this would be an actual blocking IO!) | |
) <* | |
IO.println("Start has finished!") | |
} | |
/** | |
* This function will either return a `Some(ErrorCondition)` relatively quickly if shutdown fails, | |
* or if shutdown succeeds it will run for as long as it takes to stop the running services and will then return. | |
*/ | |
def stop(): IO[Option[ErrorCondition]] = { | |
IO.println("Stop has started...") >> | |
IO.pure(failStop).ifM( | |
IO.pure(Some(ErrorCondition("STOP FAILED"))), | |
IO.delay(5.seconds) >> IO.pure(None) // Stopped OK | |
) <* | |
IO.println("Stop has finished!") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment