app/services/migrationcomponents/VSGlobalMetadata.scala (69 lines of code) (raw):

package services.migrationcomponents import java.util.UUID import akka.actor.ActorSystem import akka.http.javadsl.model.StatusCodes import akka.http.scaladsl.Http import akka.http.scaladsl.model.{HttpMethods, HttpRequest, HttpResponse, MediaRange, MediaTypes} import akka.http.scaladsl.model.headers.{Accept, Authorization, BasicHttpCredentials} import akka.stream.Materializer import akka.stream.scaladsl.{Keep, Sink} import akka.util.ByteString import org.slf4j.LoggerFactory import play.api.libs.json.{JsArray, JsValue, Json} import scala.concurrent.{ExecutionContext, Future} case class VSGlobalMetadataEntry(uuid:UUID, entries:Map[String,String]) case class VSGlobalMetadataGroup(name:String, entries:Seq[VSGlobalMetadataEntry]) { def valueFor(uuid:UUID):Option[VSGlobalMetadataEntry] = entries.find(_.uuid == uuid) } class VSGlobalMetadata(implicit actorSystem: ActorSystem, mat:Materializer, ec:ExecutionContext) { private val logger = LoggerFactory.getLogger(getClass) /** * isolate the Http request so we can mock it in testing * @param req HttpRequest to make * @return a Future containing the HttpResponse */ def makeHttpRequest(req:HttpRequest) = Http().singleRequest(req) /** * reads in and buffers the response body * @param response * @return */ private def consumeBody(response:HttpResponse):Future[ByteString] = { response.entity.dataBytes.toMat(Sink.reduce[ByteString]((acc, elem)=>acc.concat(elem)))(Keep.right).run() } def extractGlobalMetadata(vsBaseUri:String, vsUser:String, vsPasswd:String):Future[JsValue] = { val uri = s"$vsBaseUri/API/metadata" val auth = Authorization(BasicHttpCredentials(vsUser, vsPasswd)) val accept = Accept(MediaRange(MediaTypes.`application/json`)) val req = HttpRequest(HttpMethods.GET, uri, Seq(auth, accept)) makeHttpRequest(req).flatMap(response=>{ if(response.status==StatusCodes.BAD_GATEWAY || response.status==StatusCodes.GATEWAY_TIMEOUT) { response.entity.discardBytes() Future.failed(new RuntimeException("Vidispine timed out")) } else { consumeBody(response).map(serverBytes => { if (response.status != StatusCodes.OK) { logger.warn(s"Could not load global meta from Vidispine: ${serverBytes.utf8String}") throw new RuntimeException("Server error") } else { Json.parse(serverBytes.toArray) } }) } }) } /** * converts the key/value content for the given group entry to a VSGlobalMetadataEntry * @param from * @return */ def groupContent(uuidStr:String, from:JsArray) = { val uuid = UUID.fromString(uuidStr) val rawKv = for { field <- from.value valueStruct <- (field \ "value").as[JsArray].value } yield ((field \ "name").as[String], (valueStruct \ "value").as[String]) VSGlobalMetadataEntry(uuid, rawKv.toMap) } def groupForName(name:String, groups:IndexedSeq[JsValue]) = { val rawGroupEntries = for { groupEntry <- groups if (groupEntry \ "name").as[String] == name } yield ((groupEntry \ "uuid").as[String], (groupEntry \ "field").as[JsArray]) VSGlobalMetadataGroup(name, rawGroupEntries.map(tuple=>groupContent(tuple._1, tuple._2))) } def loadGroups(groupNames:Seq[String], vsBaseUri:String, vsUser:String, vsPasswd:String):Future[Seq[VSGlobalMetadataGroup]] = { extractGlobalMetadata(vsBaseUri, vsUser, vsPasswd).map(jsData=>{ val groups = ( for { timespan <- (jsData \ "timespan").as[JsArray].value } yield (timespan \ "group").as[JsArray].value ).flatten.toIndexedSeq groupNames.map(name=>groupForName(name, groups)) }) } }