Skip to content

Instantly share code, notes, and snippets.

@reneweteling
Last active August 14, 2018 09:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save reneweteling/281d4911fbc5c3b373180bfe25168c7c to your computer and use it in GitHub Desktop.
Save reneweteling/281d4911fbc5c3b373180bfe25168c7c to your computer and use it in GitHub Desktop.
Test for play application in an Lagom setup
import com.aceandtate.stock.api.StockService
import com.lightbend.lagom.scaladsl.api.{ LagomConfigComponent, ServiceAcl, ServiceInfo }
import com.lightbend.lagom.scaladsl.client.LagomServiceClientComponents
import com.lightbend.lagom.scaladsl.devmode.LagomDevModeComponents
import com.softwaremill.macwire._
import controllers.StockController
import play.api.ApplicationLoader.Context
import play.api.libs.ws.ahc.AhcWSComponents
import play.api.{ ApplicationLoader, BuiltInComponentsFromContext, Mode }
import play.filters.HttpFiltersComponents
import play.api.ApplicationLoader.Context
import play.api.libs.ws.ahc.AhcWSComponents
import play.api.mvc.EssentialFilter
import play.api.{ ApplicationLoader, BuiltInComponentsFromContext, Mode }
import play.filters.HttpFiltersComponents
import play.i18n.I18nComponents
import router.Routes
import scala.collection.immutable
import scala.concurrent.ExecutionContext
abstract class FrontendGateway(context: Context) extends BuiltInComponentsFromContext(context)
with HttpFiltersComponents
with AhcWSComponents
with LagomConfigComponent
with LagomServiceClientComponents {
override lazy val serviceInfo: ServiceInfo = ServiceInfo(
"frontend-gateway",
Map(
"frontend-gateway" -> immutable.Seq(ServiceAcl.forPathRegex("/api/v1/.*"))
)
)
override implicit lazy val executionContext: ExecutionContext = actorSystem.dispatcher
override lazy val router = {
val prefix = "/api/v1"
wire[Routes]
}
// Services
lazy val stockService = serviceClient.implement[StockService]
// Controllers
lazy val stockController = wire[StockController]
}
class FrontendGatewayLoader extends ApplicationLoader {
override def load(context: Context) = context.environment.mode match {
case Mode.Dev ⇒
(new FrontendGateway(context) with LagomDevModeComponents).application
case _ ⇒
???
}
}
# Stock management
GET /stock controllers.StockController.stockIndex
+ nocsrf
POST /stock controllers.StockController.createStock
+ nocsrf
PUT /stock/:stockId controllers.StockController.updateStock(stockId: String)
GET /stock/events controllers.StockController.stockEvents
package controllers
import com.aceandtate.stock.api.{CreateStockRequest, StockService, UpdateStockRequest}
import play.api.data.Forms._
import play.api.data.{Form, FormError}
import play.api.libs.json.{JsArray, JsValue, Json, Writes}
import play.api.mvc.{AbstractController, ControllerComponents}
import scala.concurrent.{ExecutionContext, Future}
class StockController(
stockService: StockService,
controllerComponents: ControllerComponents
)(implicit ec: ExecutionContext) extends AbstractController(controllerComponents) {
def stockIndex = Action.async { implicit req ⇒
stockService
.getAllStock()
.invoke()
.map(x ⇒ Ok(
Json.toJson(x)
))
.recoverWith { case e ⇒ Future(InternalServerError(Json.toJson(Map("cause" -> e.getLocalizedMessage)))) }
}
def createStock = Action.async { implicit req =>
StockForm.form.bindFromRequest().fold(
formWithErrors => {
Future(Ok(JsArray(formWithErrors.errors.map(FormErrorWrites.writes))))
},
createStockForm => {
stockService
.createStock()
.invoke(CreateStockRequest(createStockForm.sku, createStockForm.initialQuantity, createStockForm.location))
.map(x ⇒ Ok(
Json.toJson(x)
))
.recoverWith { case e => Future(InternalServerError(Json.toJson(Map("cause" -> e.getLocalizedMessage)))) }
}
)
}
def updateStock(stockId: String) = Action.async { implicit req =>
StockForm.form.bindFromRequest().fold(
formWithErrors => {
Future(Ok(JsArray(formWithErrors.errors.map(FormErrorWrites.writes))))
},
createStockForm => {
stockService
.updateStock(stockId)
.invoke(UpdateStockRequest(createStockForm.sku, createStockForm.initialQuantity, createStockForm.location))
.map(x ⇒ Ok)
.recoverWith { case e => Future(InternalServerError(Json.toJson(Map("cause" -> e.getLocalizedMessage)))) }
}
)
}
def stockEvents = Action.async { implicit req =>
stockService
.newStockEvents()
.invoke()
.map(x ⇒
Ok.chunked(
x.map(s =>
Json.toJson(s).toString + "\n"
)
)
)
.recoverWith { case e ⇒ Future(InternalServerError(Json.toJson(Map("cause" -> e.getLocalizedMessage)))) }
}
implicit object FormErrorWrites extends Writes[FormError] {
override def writes(o: FormError): JsValue = Json.obj(
"key" -> Json.toJson(o.key),
"message" -> Json.toJson(o.message)
)
}
}
case class StockForm(sku: String, initialQuantity: Long, location: String)
object StockForm {
val form = Form(
mapping(
"sku" -> nonEmptyText,
"initialQuantity" -> longNumber,
"location" -> nonEmptyText,
)(StockForm.apply)(StockForm.unapply _)
)
}
import controllers.StockController
import org.scalatestplus.play.PlaySpec
import play.api.mvc.{Result, Results}
import play.api.test.FakeRequest
import scala.concurrent.Future
class StockControllerSpec extends PlaySpec with Results {
"Stock index" should {
"be valid" in {
// Ok.... so how do i inject, of provide the stockService and controllerComponents??
val controller = new StockController(stockService, controllerComponents)
val result: Future[Result] = controller.stockIndex().apply(FakeRequest())
val body = contentAsString(result)
body mustBe "ok"
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment