Lightbend Lagom and Akka: Unable to hit rest endpoint of lagom services - scala

I am creating lagom simple application, with define one rest end point and hit end point using rest client postman. But In response I am getting, action not found error. I am integrate Akka with lagom, Following is my code:
Service:
trait TwitterSchedulerService extends Service {
def doWork: ServiceCall[NotUsed, Done]
override def descriptor: Descriptor = {
import Service._
named("scheduler").withCalls(
call(doWork)
)
}
}
ServiceImpl:
class TwitterSchedulerServiceImpl(system: ActorSystem) extends TwitterSchedulerService {
override def doWork = ServiceCall { _ =>
Future {
println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ")
Done
}
}
}
Loader Configuration :
class TwitterLoader extends LagomApplicationLoader {
override def load(context: LagomApplicationContext): LagomApplication =
new TwitterApplication(context) {
override def serviceLocator = NoServiceLocator
}
}
object TwitterSerializerRegistry extends JsonSerializerRegistry {
override val serializers = Vector(
JsonSerializer[String]
)
}
abstract class TwitterApplication(context: LagomApplicationContext) extends LagomApplication(context)
with CassandraPersistenceComponents with AhcWSComponents {
override lazy val lagomServer = LagomServer.forServices(
bindService[TwitterSchedulerService].to(wire[TwitterSchedulerServiceImpl])
)
override lazy val jsonSerializerRegistry = TwitterSerializerRegistry
}
I am following lagom documentation http://www.lagomframework.com/documentation/1.3.x/scala/Akka.html. I want to know, why this error occur, event all rest points are defined???

Your service is running at http://localhost:57211
http://localhost:9000 is running a Service Gateway server that acts as a reverse proxy to all of the services running in your project.
The Service Gateway can be configured to forward service calls onto your service, but it does not by default. You configure it by defining ACLs (access control lists) in your service descriptors.
Most commonly, you'll call withAutoAcl(true) to automatically forward all service call paths to your service:
trait TwitterSchedulerService extends Service {
def doWork: ServiceCall[NotUsed, Done]
override def descriptor: Descriptor = {
import Service._
named("scheduler").withCalls(
call(doWork)
).withAutoAcl(true)
}
}
If you want more control over which paths get forwarded from the Service Gateway to the back-end service, you can call withAcls to pass a list of explicit methods and path regular expressions that should be forwarded from the Service Gateway:
trait TwitterSchedulerService extends Service {
def doWork: ServiceCall[NotUsed, Done]
override def descriptor: Descriptor = {
import Service._
named("scheduler").withCalls(
call(doWork)
).withAcls(
ServiceAcl.forPathRegex("/doWork")
)
}
}
If you deploy to ConductR (part of the Lightbend Production Suite), these ACL configurations in your service descriptor are also used to generate ConductR ACL configuration.

Related

Change jdbcURL in fakeApplication in unit test

I am new to scala and I am using it with Playframework to build an API.
I have multiple docker containers (API, DB). Also, there is a module where DB configuration are specified. In this configuration, I use docker container name db in setJdbcUrl.
Everything works perfectly when running things within docker containers.
However, I have a healthcheck unit test that tests a route.
This test fails because it cant recognize the URL (as expected), but I am not sure if there is a way to update the fake application in the test to use the correct URL.
The Module
class ReadWriteDB extends ScalaModule {
#Singleton
#Provides
#Named("db.sql.readwrite.quill.context")
def quillContext(): PostgresJdbcContext[SnakeCase.type] = {
val ds = new HikariDataSource(DBConnectionConfig.hikariConfig())
QuillFactory.create(ds, Duration("2m"))
}
object DBConnectionConfig {
def hikariConfig(): HikariConfig = {
val config = new HikariConfig
config.setDriverClassName("org.postgresql.Driver")
config.setJdbcUrl("jdbc:postgresql://db:5432/postgres")
// config.setJdbcUrl("jdbc:postgresql://localhost:5432/postgres")
// with the commented config, unit test runs correctly
config.setUsername(...)
config.setPassword(...)
config
}
}
The unit test
class HealthCheckSpec extends PlaySpec with GuiceOneAppPerTest with Injecting {
"HealthCheck status" should {
"reply from the router" in {
// can I edit app to use a different URL here?
val request = FakeRequest(GET, "/status")
val home = route(app, request).get
status(home) mustBe OK
contentAsString(home) must include("API is running!")
}
}
}
Controller
#Singleton
class HealthCheck #Inject()(val controllerComponents: ControllerComponents) extends BaseController {
def check(): Action[AnyContent] = Action {
Ok("API is running!\n")
}
}
Any guidelines would be appreciated.
You have all the tools you need. I would turn the object into a class and use #provides and #singleton like you have in your quillContext, and I'd also pull out the behaviour into a trait and have a method for just is the healthcheck okay:
trait DBObjectConfig {
def hikariConfig(): HikariConfig
def isOK() :Boolean
}
#provides
class RealDBConnection extends DBObjectConfig {
def hikariConfig(): HikariConfig = {
val config = new HikariConfig
config.setDriverClassName("org.postgresql.Driver")
config.setJdbcUrl("jdbc:postgresql://db:5432/postgres")
config.setJdbcUrl("jdbc:postgresql://localhost:5432/postgres")
config.setUsername(...)
config.setPassword(...)
config
}
// todo: make something here that's a reliable test.
def isOK() :Boolean = {
Option(hakariConfig.getConnection()).isDefined
}
}
Your healthcheck controller can use isOK() to see if the db is up or not.
Then in your test, using Guice (your test extends an injector?) you should be able to bind a fake implementation of DBObjectConfig to your app, which will cause your controller to not instantiate a real db connection in the testing environment:
// inside your tests.
class MockDB extends DBObjectConfig {
def hikariConfig(): HikariConfig = ??? // implementation not required
def isOK() :Boolean = true // fake it being okay.
}
val application = new GuiceApplicationBuilder()
.overrides(bind[DBObjectConfig].to[MockDB])
.build()
Some technique using this general approach should work.
To implement Health Check I would recommend Play-Actuator dependency.

How to create a lagom server with grpc service implementation only?

I am trying to create a grpc service using Lagom framework following this documentation. Here the hello service also exposes rest API besides grpc service.
Now while creating lagom server in the ApplicationLoader we assign grpc service impl as additionalRouter like so:
abstract class HelloApplication(context: LagomApplicationContext)
extends LagomApplication(context)
with AhcWSComponents {
// Bind the service that this server provides
override lazy val lagomServer =
serverFor[HelloService](wire[HelloServiceImpl])
.additionalRouter(wire[HelloGrpcServiceImpl])
}
Its all fine for the purpose of demo but we may not need to always create a REST endpoint besides gRPC endpoint. In that case I won't need either HelloService or HelloServiceImpl. The problem is how would you create lagom server with only HelloGrpcServiceImpl? I can't see to find a way either any documentation or the APIs itself to be able to achieve this!
Please suggest.
Based on the answer in the link I provided as a comment to my question, the solution would look something like this:
trait PrimeGeneratorService extends Service {
override def descriptor: Descriptor = named("prime-generator")
}
class PrimeGeneratorServiceImpl() extends PrimeGeneratorService
abstract class PrimeGeneratorApplication(context: LagomApplicationContext)
extends LagomApplication(context) with AhcWSComponents {
override lazy val lagomServer: LagomServer = {
serverFor[PrimeGeneratorService](wire[PrimeGeneratorServiceImpl])
.additionalRouter(wire[PrimeGeneratorGrpcServiceImpl])
}
}
class PrimeGeneratorLoader extends LagomApplicationLoader {
override def load(context: LagomApplicationContext): LagomApplication =
new PrimeGeneratorApplication(context) {
override def serviceLocator: ServiceLocator = NoServiceLocator
}
override def loadDevMode(context: LagomApplicationContext): LagomApplication =
new PrimeGeneratorApplication(context) with LagomDevModeComponents
override def describeService = Some(readDescriptor[PrimeGeneratorService])
}
Here we have to create dummy service trait and implementation, namely, PrimeGeneratorService and PrimeGeneratorServiceImpl, respectively.
Don't forget to add following in the loader class:
override def describeService = Some(readDescriptor[PrimeGeneratorService])

Guice dependency requiring references to Singleton inside AbstractModule

I have an external library (scala-redis) that requires an implicit ActorSystem when initializing the client. I would like to have my RedisClient as a Singleton inside my Play (2.6) Application because it would make sense having it as a Singleton.
class CustomAppModule(environment: Environment,
configuration: Configuration) extends AbstractModule {
def configure() = {
//val system = getProvider(classOf[ActorSystem]).get()
//val system = ActorSystem()
//bind(classOf[ActorSystem]).toInstance(system)
val redis = RedisClient(configuration.get[String]("redis.host"))(system)
bind(classOf[RedisClient]).toInstance(redis)
}
}
First system fails because of "Provider cannot be used until the Injector has been created", and second system fails because Play Framework initializes the ActorSystem itself when the application starts, and second system fails because of "binding to akka.actor.ActorSystem was already configured at play.api.inject.BuiltinModule".
So what would be the idiomatic way with Guice/DI to proceed with this kind of situation? Do I need a wrapper Singleton that has the RedisClient as a value, and where the ActorSystem can be injected?
I think the provides method will solve your problem. Write your module as
class MyModule extends AbstractModule {
def configure() = {
}
#Provides
#Singleton
def givePrecious() : MyClass = {
new MyClass()
}
}
Here my Class looks like
#Singleton
class MyClass(a: String) {
def this() = {
this("a")
println("constructor called")
}
}
Now I try to create 3 instances of this class
val injector = Guice.createInjector(new MyModule())
val precious1 = injector.getInstance(classOf[MyClass])
val precious2 = injector.getInstance(classOf[MyClass])
val precious3 = injector.getInstance(classOf[MyClass])
You will see that the string "constructor called" is printed only once.
For sake of simplicity I have make a as a string. you can try to make it an instance of ActorSystem.

How to not bind to localhost with ScalatestRouteTest

I want to test routes with ScalatestRouteTest as follows:
trait MyRoutes extends Directives {
self: Api with ExecutionContextProvider =>
val myRoutes: Route =
pathPrefix("api") {
path("") {
(get & entity(as[MyState])) {
request => {
complete(doSomething(request.operation))
}
}
}
}
}
}
class RoutesSpec extends WordSpecLike with Api with ScalatestRouteTest
with Matchers with MyRoutes with MockitoSugar {
"The Routes" should {
"return status code success" in {
Get() ~> myRoutes ~> check {
status shouldEqual StatusCodes.Success
}
}
}
}
When running the test I get the runtime error:
Could not run test MyRoutesSpec: org.jboss.netty.channel.ChannelException: Failed to bind to: /127.0.0.1:2552
I don't want to bind to the localhost. How can this be accomplished?
The solution was to disable remoting and clustering (this was enabled in a separate configuration file) and use the default provider.
The actor remoting and clustering conflicts with the running application (started for the routing test). They pick up the same configuration and therefore both try to use the same port which conflicts.
The following code was added in trait MyRoutes to make it work:
// Quick hack: use a lazy val so that actor system can "instantiate" it
// in the overridden method in ScalatestRouteTest while the constructor
// of this class has not yet been called.
lazy val routeTestConfig =
"""
| akka.actor.provider = "akka.actor.LocalActorRefProvider"
| persistence.journal.plugin = "akka.persistence.journal.inmem"
""".stripMargin
override def createActorSystem(): ActorSystem =
ActorSystem("RouteTest", ConfigFactory.parseString(routeTestConfig))

Spray REST API Application Structure - Suggestions Needed

I have the following multi-module project structure built using sbt:
myProject-api
myProject-core
myProject-core is organized as below:
It contains certain actors which acts as a facade to my services. For example., I have a UserActor that sits in front of a UserService. A NotificationActor that sits in front of a NotificationService and so on.
I have another trait that exposes there actors to anybody that is interested:
trait MyProjectCoreActors {
def myAppCfg = MyProjConfig.appCfg
def userActor = myAppCfg.userActor
def notifyActor = myAppCfg.notifyActor
}
object MyProjectCoreActors {
... some initialization routing that initializes the MyProjConfig
}
My UserActor is thus defined as:
class UserActor(service: UserService) extends Actor {
...
...
}
My UserService is as follows:
class UserService(dbConfig: DbConfig) {
...
...
}
I have another class called MyProjectConfig which I initialize using the application.conf file. In this file I have the connection details to the database and so on. The MyProjectConfig is initialized as below:
trait MyProjectConfig {
def actorSystem: ActorSystem
// the actors
def userActor: ActorRef
}
object MyProjectConfig {
def apply(appConfig: Config, system: ActorSystem): MyProjectConfig = {
new MyProjectConfig {
private val dbConfig = loadDBConfig(appConfig)
override val actorSystem = system
// each actor gets its own DBConfigInstance instance
override val userActor =
actorSystem.actorOf(
Props(new UserActor(UserService(dbConfig)))
)
}
}
}
I have now the Spray routing as defined below:
trait MyProjectService extends HttpService with MyProjectCoreActors {
def pingRoute = path("ping") {
get {
userActor ! "newUser"
complete("pong!")
}
}
def pongRoute = path("pong") {
get { complete("pong!?") }
}
def route = pingRoute ~ pongRoute
}
What is now missing is a way to call the MyProjectConfig.apply(....) method and pass in the Actor System and the underlying application.conf!
This was originally a Play based application, where I had a Lifecycle plug in which had access to the underlying Application from where I got the config and the actor system. How could I now get the same here with Spray?
I have a Boot class that looks like this:
object MyBootHttpService extends App {
implicit val actorSystem = ActorSystem("myproj-actor-system")
}
How could I pass this ActorSytem to MyProjectConfig.apply(....)? and from where could I get the application.conf?
I think you can do such things (DI) in your MyBootHttpService class.
For example
object MyBootHttpService extends App {
implicit val actorSystem = ActorSystem("myproj-actor-system")
private val config = ConfigFactory.load
private val myAppConfig = MyProjectConfig(config, actorSystem)
// Initialise classes that depend on config and actorsystem....
private val service = new MyProjectService with HttpServiceActor {
override implicit val actorRefFactory = actorSystem
}
// Bind our service
IO(Http) ? Bind(listener = service, interface = "0.0.0.0", port = config.getInt("port"))
}
Typesafe config library object ConfigFactory is generally used to load config files. ConfigFactory.load with no args will try and load application.conf from the classpath.