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

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])

Related

Creating a AbstractModule to inject a dependency for a 3rd party library

I have a 3rd party library that I am trying to inject the configuration into the constructor.
This is what I need to do:
class MyModule(configuration: Configuration) extends AbstractModule {
override def configure(): Unit = {
bind(classOf[TwitterApi])
.to(classOf[MyTwitterApi])
.asEagerSingleton
}
}
The constructor of MyTwitterApi doesn't take a Play.api.Configuration but a typesafe.config.Config
class MyTwitterApi(config: Config) ...
So I need to do pass configuration.underlying to my constructor, how is this possible using DI in this AbstractModule?
I need this instance to be a singleton also.
You can use provider to setup your module with eagerSingleton
import com.google.inject.{AbstractModule, Provider}
class MyModule(configuration: Configuration) extends AbstractModule {
override def configure(): Unit = {
val twitterApiProvider: Provider[TwitterApi] =
() => new MyTwitterApi(configuration.underlying)
bind(classOf[TwitterApi])
.toProvider(twitterApiProvider)
.asEagerSingleton
}
}
You can find a working example with sample classes at - https://scastie.scala-lang.org/sarveshseri/ujwvJJNnTpiWDqdkBJQoFw/2
I think you want something like this:
class MyModule(configuration: Configuration) extends AbstractModule {
override def configure(): Unit = {
val myTwitterApiInstance = new MyTwitterApi(configuration.underlying)
bind(classOf[TwitterApi])
.toInstance(myTwitterApiInstance)
}
}
Or another approach would be to provide a binding for Config but if your MyTwitterApi doesn't have #Inject annotation this won't help.

Is it possible to use scala self types with guice?

I have a controller:
class HomeController #Inject() (cc: ControllerComponents) extends AbstractController(cc)with Logging
{
this: SecuredActionByToken =>
def index = CheckedToken{ ...
Where SecuredActionByToken trait is using class names CheckTokenService to verify that user can run 'index' (we are trying to move from kind of cake pattern to Guice).
Is it possible to inject CheckTokenService to SecuredActionByToken? Of course, I could inject it to HomeController itself and work somehow with it, but I don't really want to push some services to controllers when they don't directly use them.
i ended up creating my own provider for this bean, this way I can inject service directly to provider and use it in overriden trait method:
class HomeControllerFactory #Inject()
(controllerComponents: ControllerComponents, cts: CheckTokenService, dbConfigProvider: DatabaseConfigProvider)
extends Provider[HomeController]{
override def get(): HomeController = {
new HomeController(controllerComponents) with SecuredActionByToken {
override def checkTokenService: CheckTokenService = cts
}
}
}
plus binding:
bind(classOf[HomeController]).toProvider(classOf[HomeControllerFactory])
I can also test is by creating homeController like this:
val testCheckTokenService =...
val homeController = new HomeController(stubControllerComponents()) with SecuredActionByToken {
override def checkTokenService: CheckTokenService = testCheckTokenService....
}

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

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.

Custom Router in Playframework 2.4

I'm using Play 2.4. I'd like to replace the default router, with my own class, using the new dynamic dependency injection play feature. What are the steps to do that?
One possible solution would be to create a new Guice Module, to bind your new router:
class RouterModule extends AbstractModule {
override def configure(): Unit = {
bind(classOf[Router]).to(classOf[CustomRouter])
}
}
Then define a new Application Loader, which will override the default configured router, by using the newly created module:
class MyApplicationLoader extends GuiceApplicationLoader with GuiceableModuleConversions {
override protected def overrides(context: Context): Seq[GuiceableModule] = {
Seq(fromGuiceModule(new RouterModule)) ++ super.overrides(context)
}
}
And use the newly created application loader, instead of the default one, in application.conf:
play.application.loader = "de.zalando.store.pdp.modules.MyApplicationLoader"

How to setup my mock Dao classes when using Guice?

A typical service looks like this:
trait BaseService extends LazyLogging {
def getDb() = {
DatabaseHelper.getDb // database for the scala slick library
}
}
abstract class UserService extends BaseService {
def getById(userId: Int): Option[User]
}
class UserServiceImpl #Inject(val userDao: UserDao) extends UserService = {
def getById(userId: Int): Option[User] = {
getDb().withSession { implicit session =>
return userDao.getById(userId)
}
}
}
Using Guice I wire up my objects like:
class ServiceModule extends ScalaModule {
def configure() {
bind[UserDao].to[UserDaoImpl]
bind[UserService].to[UserServiceImpl]
}
}
Now when I am unit testing using scalatest, I am a bit confused how I can de-couple the database access since I want to mock the database responses.
My spec looks like:
class UserServiceSpec extends UnitSpec with MockitoSugar {
val userService = injector.getInstance(classOf[UserService])
describe("UserServiceSpec") {
it("should do someting") {
val abc = userService.doSomething();
abc.name should be("abc")
}
}
}
My UnitSpec class wires up my Guice.
I am confused, where should I create the mock objects (using mockito) and how should I wire them using Guice? In the ServiceModule or?
My design seems wrong since my BaseService has a connection to the database, I need to refactor that out somehow.
Looking for a way to get out of this bad design I currently seem to have, ideas?
You can move the db connection to DAO layer. Your application should have three layers: controller -> service -> DAO. All the service layer needs to know is functionalities provided by DAO, i.e. CRUD operations; it should not know anything about db connection since that is DAO's responsibility.
I am not really sure about Slick framework, but for Play framework with Guice, it allows to disable "real" bindings (injected dependencies) that you expect when the application is running and enable bindings that are only for testing like this:
implicit override lazy val app = new GuiceApplicationBuilder()
.configure(appConfig)
.disable(classOf[ReactiveMongoModule], classOf[CommonModule])
.bindings(bind(classOf[ReactiveMongoApi]).toInstance(api))
.bindings(TestDaoModule())
.build()
This is a complete integration test (controller layer), hope that it helps: https://github.com/luongbalinh/play-mongo/blob/master/test/controllers/UserControllerTest.scala