app/controllers/ApiResult.scala (158 lines of code) (raw):

package controllers import scala.language.postfixOps import agent.{CrawlRate, Label, Origin, ResourceType} import model.DataContainer import org.joda.time.DateTime import play.api.http.{ContentTypes, Status} import play.api.libs.json._ import play.api.mvc.{RequestHeader, Result, Results} import scala.concurrent.{ExecutionContext, Future} import scala.util.Try import utils.{Logging, ResourceFilter} import jsonimplicits.joda._ import scala.concurrent.duration._ import scala.language.postfixOps // use this when the API call has illegal parameters case class ApiCallException(failure: JsObject, status: Int = Status.BAD_REQUEST) extends RuntimeException( failure.fields.map(f => s"${f._1}: ${f._2}").mkString("; ") ) object ApiResult extends Logging { val noSourceContainer: DataContainer = new DataContainer { val name = "no data source" def lastUpdated: DateTime = new DateTime val isStale = false } def addCountToJson(data: JsValue): JsValue = { data match { case JsObject(fields) => JsObject(fields.flatMap { case (key, value) => value match { case JsArray(array) => List( (s"$key.length", JsNumber(array.size)), (key, addCountToJson(value)) ) case _ => List((key, addCountToJson(value))) } }) case JsArray(values) => JsArray(values.map(addCountToJson)) case other => other } } object filter { import jsonimplicits.model.labelWriter case class SourceData[D](sourceData: Try[Map[Label, Seq[D]]]) { def reduce(reduce: Map[Label, Seq[D]] => JsValue)(implicit request: RequestHeader, ec: ExecutionContext ): Future[Result] = { reduceAsync(input => Future.successful(reduce(input))) } def reduceAsync(reduce: Map[Label, Seq[D]] => Future[JsValue])(implicit request: RequestHeader, ec: ExecutionContext ): Future[Result] = { sourceData.map { mapSources => val filter = ResourceFilter.fromRequest val filteredSources = mapSources.groupBy { case (label, _) => filter.isMatch(label.origin.filterMap) } filteredSources .get(false) .foreach(falseMap => if (falseMap.values.exists(_.isEmpty)) log.warn( s"The origin filter contract map has been violated: data exists in a discarded source - ${request.uri} from ${request.remoteAddress}" ) ) val sources: Map[Label, Seq[D]] = filteredSources.getOrElse(true, Map.empty) val usedLabels = sources.filter { case (_, data) => data.nonEmpty }.keys val staleLabels = sources.keys.filter { label => label.bestBefore.isStale } val lastUpdated: DateTime = usedLabels.toSeq.filterNot(_.isError).map(_.createdAt) match { case dates: Seq[DateTime] if dates.nonEmpty => dates.min((x: DateTime, y: DateTime) => { x.getMillis.compareTo(y.getMillis) }) case _ => new DateTime(0) } val stale = sources.keys.exists(_.bestBefore.isStale) val reduceSources = reduce(sources).map { data => val dataWithMods = if (request.getQueryString("_length").isDefined) addCountToJson(data) else data val json = Json.obj( "status" -> "success", "lastUpdated" -> lastUpdated, "stale" -> stale, "staleSources" -> staleLabels, "data" -> dataWithMods, "sources" -> usedLabels ) request.getQueryString("_pretty") match { case Some(_) => Results.Ok(Json.prettyPrint(json)).as(ContentTypes.JSON) case None => Results.Ok(json) } }(ec) reduceSources } recover { case ApiCallException(failure, status) => Future.successful( Results.Status(status)( Json.obj( "status" -> "fail", "data" -> failure ) ) ) case e: Exception => Future.successful( Results.InternalServerError( Json.obj( "status" -> "error", "message" -> e.getMessage, "stacktrace" -> e.getStackTrace.map(_.toString) ) ) ) } get } } def apply[D](mapSources: => Map[Label, Seq[D]]): SourceData[D] = { new SourceData[D]( Try { mapSources } ) } } def noSource( block: => JsValue )(implicit request: RequestHeader, ec: ExecutionContext): Future[Result] = { val sourceLabel: Label = Label( ResourceType(noSourceContainer.name), new Origin { val account = "unknown" val vendor = "unknown" val resources = Set.empty[String] val jsonFields = Map.empty[String, String] val crawlRate = Map(noSourceContainer.name -> CrawlRate(15 minutes, 1 minutes)) override def toMarkerMap: Map[String, Any] = jsonFields }, 1, noSourceContainer.lastUpdated ) filter(Map(sourceLabel -> Seq("dummy"))) reduceAsync { _ => Future.successful(block) } } }