Application.scala 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. package controllers
  2. import java.util.concurrent.ThreadLocalRandom
  3. import javax.inject.{Singleton, Inject}
  4. import play.api.libs.json.{JsObject, Json, JsValue}
  5. import play.api.libs.concurrent.Execution.Implicits.defaultContext
  6. import reactivemongo.api.ReadPreference
  7. import scala.concurrent.Future
  8. import play.api.mvc.{ Action, Controller }
  9. import play.modules.reactivemongo.{
  10. MongoController, ReactiveMongoApi, ReactiveMongoComponents
  11. }
  12. import play.modules.reactivemongo.json._
  13. import play.modules.reactivemongo.json.collection.JSONCollection
  14. @Singleton
  15. class Application @Inject() (val reactiveMongoApi: ReactiveMongoApi)
  16. extends Controller with MongoController with ReactiveMongoComponents {
  17. private def worldCollection: JSONCollection = reactiveMongoApi.db.collection[JSONCollection]("world")
  18. private def fortuneCollection: JSONCollection = reactiveMongoApi.db.collection[JSONCollection]("fortune")
  19. private val projection = Json.obj("_id" -> 0)
  20. def getRandomWorlds(queries: Int): Future[Seq[Option[JsObject]]] = {
  21. val futureWorlds: Seq[Future[Option[JsObject]]] = for {
  22. _ <- 1 to queries
  23. } yield { worldCollection
  24. .find(Json.obj("_id" -> getNextRandom), projection)
  25. .one[JsObject]
  26. }
  27. Future.sequence(futureWorlds)
  28. }
  29. def getRandomWorld = {
  30. val futureWorld = worldCollection
  31. .find(Json.obj("id" -> getNextRandom), projection)
  32. .one[JsValue]
  33. futureWorld
  34. }
  35. def getFortunes: Future[List[JsObject]] = {
  36. val futureFortunes: Future[List[JsObject]] =
  37. fortuneCollection.find(Json.obj())
  38. .cursor[JsObject](ReadPreference.primaryPreferred, false).collect[List]()
  39. futureFortunes
  40. }
  41. def updateWorlds(queries: Int): Future[Seq[Option[JsObject]]] = {
  42. val futureWorlds: Future[Seq[Option[JsObject]]] = getRandomWorlds(queries)
  43. val futureNewWorlds: Future[Seq[Option[JsObject]]] = futureWorlds.map( worlds => {
  44. worlds.map(worldOption => {
  45. worldOption.map(world => {
  46. val newWorld = world ++ Json.obj("randomNumber" -> getNextRandom)
  47. worldCollection.update(world, newWorld)
  48. newWorld
  49. })
  50. })
  51. })
  52. futureNewWorlds
  53. }
  54. def getNextRandom: Int = {
  55. ThreadLocalRandom.current().nextInt(TestDatabaseRows) + 1
  56. }
  57. // Test seems picky about headers. Doesn't like character set being there for JSON. Always wants Server header set.
  58. // There is a Filter which adds the Server header for all types. Below I set Content-Type as needed to get rid of
  59. // warnings.
  60. // Easy ones
  61. case class HelloWorld(message: String)
  62. def getJsonMessage = Action {
  63. val helloWorld = HelloWorld(message = "Hello, World!")
  64. Ok(Json.toJson(helloWorld)(Json.writes[HelloWorld])).withHeaders(CONTENT_TYPE -> "application/json")
  65. }
  66. val plaintext = Action {
  67. // default headers are correct according to docs: charset included.
  68. // BUT the test harness has a WARN state and says we don't need it.
  69. Ok("Hello, World!").withHeaders(CONTENT_TYPE -> "text/plain")
  70. }
  71. // Semi-Common code between Scala database code
  72. protected val TestDatabaseRows = 10000
  73. def doDb = Action.async {
  74. getRandomWorld.map { worlds =>
  75. Ok(Json.toJson(worlds.head)).withHeaders(CONTENT_TYPE -> "application/json")
  76. }
  77. }
  78. def queries(countString: String) = Action.async {
  79. val n = parseCount(countString)
  80. getRandomWorlds(n).map { worlds =>
  81. Ok(Json.toJson(worlds)).withHeaders(CONTENT_TYPE -> "application/json")
  82. }
  83. }
  84. private def byMessage(item: JsValue): String = {
  85. (item \ "message").as[String]
  86. }
  87. def fortunes() = Action.async {
  88. getFortunes.map { dbFortunes =>
  89. val appendedFortunes = Json.obj("_id" -> 0, "message" -> "Additional fortune added at request time.") :: dbFortunes
  90. val sorted = appendedFortunes.sortBy(byMessage(_))
  91. Ok(views.html.fortune(sorted)).withHeaders(CONTENT_TYPE -> "text/html")
  92. }
  93. }
  94. def update(queries: String) = Action.async {
  95. val n = parseCount(queries)
  96. updateWorlds(n).map { worlds =>
  97. Ok(Json.toJson(worlds)).withHeaders(CONTENT_TYPE -> "application/json")
  98. }
  99. }
  100. private def parseCount(s: String): Int = {
  101. try {
  102. val parsed = java.lang.Integer.parseInt(s, 10)
  103. parsed match {
  104. case i if i < 1 => 1
  105. case i if i > 500 => 500
  106. case i => i
  107. }
  108. } catch {
  109. case _: NumberFormatException => 1
  110. }
  111. }
  112. }