All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gu.vidispineakka.streamcomponents.VSGetItem.scala Maven / Gradle / Ivy

package com.gu.vidispineakka.streamcomponents

import akka.stream.{Attributes, FlowShape, Inlet, Materializer, Outlet}
import akka.stream.stage.{AbstractInHandler, AbstractOutHandler, GraphStage, GraphStageLogic}
import org.slf4j.LoggerFactory
import com.gu.vidispineakka.vidispine.{VSCommunicator, VSFile, VSLazyItem, VSShape}

import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success}

class NotFoundError(message:String) extends Throwable {
  override def getMessage: String = message
}

class VSGetItem(fieldList:Seq[String], includeShapes:Boolean = false)(implicit comm:VSCommunicator, mat:Materializer, ec:ExecutionContext) extends GraphStage[FlowShape[VSFile, (VSFile, Option[VSLazyItem])]] {
  private final val in:Inlet[VSFile] = Inlet.create("VSGetItem.in")
  private final val out:Outlet[(VSFile, Option[VSLazyItem])] = Outlet.create("VSGetItem.out")
  private val logger = LoggerFactory.getLogger(getClass)

  override def shape = FlowShape.of(in, out)

  def loadVSShapeData(itemId:String) = {
    comm.request(VSCommunicator.OperationType.GET,
      s"/API/item/$itemId",
      None,
      Map("Accept"->"application/xml"),
      Map("content"->"shape")
    ).map({
      case Left(httpErr)=>
        logger.error(s"Could not get shape data for $itemId: ${httpErr.errorCode} ${httpErr.message}")
        throw new NotFoundError("Could not get shape data")
      case Right(xmlString)=>
        val parsedContent = scala.xml.XML.loadString(xmlString)
        val shapes = (parsedContent \ "shape").map(VSShape.fromXml)
        val shapesMap = shapes.map(entry=>entry.tag->entry).toMap
        if(shapesMap.isEmpty) None else Some(shapesMap)
    })
  }

  /**
    * create a new VSLazyItem. Included like this to make testing easier.
    * @param itemId
    * @return
    */
  def makeItem(itemId:String) = VSLazyItem(itemId)

  override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) {
    private val logger:org.slf4j.Logger = LoggerFactory.getLogger(getClass)
    val completedCb = createAsyncCallback[(VSFile, Option[VSLazyItem])](itm=>push(out,(itm._1,itm._2)))
    val failedCb = createAsyncCallback[Throwable](err=>failStage(err))

    setHandler(in, new AbstractInHandler {
      override def onPush(): Unit = {
        val elem = grab(in)
        logger.debug(s"Got incoming item $elem")

        elem.membership match {
          case Some(fileItemMembership)=>
            val item = makeItem(fileItemMembership.itemId)

            val itemWithShapesFut = if(includeShapes) {
              loadVSShapeData(fileItemMembership.itemId).map(shapeinfo=>item.copy(shapes = shapeinfo))
            } else {
              Future(item)
            }

            itemWithShapesFut
              .flatMap(_.getMoreMetadata(fieldList)
                .map({
                  case Left(metadataError)=>
                    logger.error(s"Could not get metadata for item ${fileItemMembership.itemId}: $metadataError")
                    metadataError.httpError match {
                      case None=>
                        failedCb.invoke(new RuntimeException("Could not lookup metadata"))
                      case Some(httpError)=>
                        if(httpError.errorCode==404){
                          completedCb.invoke((elem, None))
                        } else {
                          failedCb.invoke(new RuntimeException("Could not lookup metadata"))
                        }
                    }
                  case Right(updatedItem)=>
                    logger.debug(s"Looked up metadata for item ${updatedItem.itemId}")
                    completedCb.invoke((elem, Some(updatedItem)))
                })
              ).recover({
              case _:NotFoundError=>
                logger.error(s"No item found for ${fileItemMembership.itemId}")
                completedCb.invoke((elem, None))
              case err:Throwable=>
                logger.error(s"Get metadata crashed for item ${fileItemMembership.itemId}")
                failedCb.invoke(err)
            })
          case None=>
            logger.warn(s"Can't look up item metadata for file ${elem.vsid} as it is not a member of any item")
            completedCb.invoke((elem, None))
        }
      }
    })

    setHandler(out, new AbstractOutHandler {
      override def onPull(): Unit = pull(in)
    })
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy