Skip to content

Commit 18ec388

Browse files
committed
expose new parentAndChildDetails property on an image response, which contains a map of the various relationships the image has with other images (with detail for each such as thumbnail)
1 parent 0cd5c0e commit 18ec388

3 files changed

Lines changed: 45 additions & 12 deletions

File tree

media-api/app/controllers/MediaApi.scala

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package controllers
22

33
import org.apache.pekko.stream.scaladsl.StreamConverters
44
import com.google.common.net.HttpHeaders
5+
import com.gu.mediaservice.lib.ImageStorageProps
56
import com.gu.mediaservice.{GridClient, JsonDiff}
67
import com.gu.mediaservice.lib.argo._
78
import com.gu.mediaservice.lib.argo.model.{Action, _}
@@ -15,6 +16,7 @@ import com.gu.mediaservice.lib.logging.{LogMarker, MarkerMap}
1516
import com.gu.mediaservice.lib.metadata.SoftDeletedMetadataTable
1617
import com.gu.mediaservice.lib.play.RequestLoggingFilter
1718
import com.gu.mediaservice.model._
19+
import com.gu.mediaservice.model.usage.{DerivativeUsageStatus, ReplacedUsageStatus}
1820
import com.gu.mediaservice.syntax.MessageSubjects
1921
import lib._
2022
import lib.elasticsearch._
@@ -573,6 +575,14 @@ class MediaApi(
573575
val deleteImagePermission = authorisation.isUploaderOrHasPermission(request.user, source.instance.uploadedBy, DeleteImagePermission)
574576
val deleteCropsOrUsagePermission = canUserDeleteCropsOrUsages(request.user)
575577

578+
val getRelationDetails = elasticSearch.getRelationDetails(id, imageResponse.getSecureThumbUrl)_
579+
val relationDetails = Map(
580+
"Replacement for" -> source.instance.identifiers.get(ImageStorageProps.replacesMediaIdIdentifierKey).map(getRelationDetails),
581+
"Replaced by" -> source.instance.usages.filter(_.status == ReplacedUsageStatus).flatMap(_.childUsageMetadata.map(_.childMediaId)).map(getRelationDetails),
582+
"Derivative of" -> source.instance.identifiers.get(ImageStorageProps.derivativeOfMediaIdsIdentifierKey).toList.flatMap(_.split(",").map(_.trim)).map(getRelationDetails),
583+
"Derivatives" -> source.instance.usages.filter(_.status == DerivativeUsageStatus).flatMap(_.childUsageMetadata.map(_.childMediaId)).map(getRelationDetails)
584+
).view.mapValues(_.iterator.toMap).toMap
585+
576586
val (imageData, imageLinks, imageActions) = imageResponse.create(
577587
id,
578588
source,
@@ -583,8 +593,12 @@ class MediaApi(
583593
request.user.accessor.tier
584594
)
585595

586-
Some((source.instance, imageData, imageLinks, imageActions))
587-
596+
Some((
597+
source.instance,
598+
imageData.asInstanceOf[JsObject] + ("parentAndChildDetails" -> Json.toJson(relationDetails)),
599+
imageLinks,
600+
imageActions
601+
))
588602
case _ => None
589603
}
590604
}

media-api/app/lib/ImageResponse.scala

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,11 @@ class ImageResponse(config: MediaApiConfig, s3Client: S3Client, usageQuota: Usag
7575

7676
val pngFileUri = image.optimisedPng.map(_.file)
7777

78-
val fileUri = image.source.file
79-
80-
val imageUrl = s3Client.signUrl(config.imageBucket, fileUri, image, imageType = Source)
78+
val imageUrl = s3Client.signUrl(config.imageBucket, image.source.file, image, imageType = Source)
8179
val pngUrl: Option[String] = pngFileUri
8280
.map(s3Client.signUrl(config.imageBucket, _, image, imageType = OptimisedPng))
8381

84-
def s3SignedThumbUrl = s3Client.signUrl(config.thumbBucket, fileUri, image, imageType = Thumbnail)
85-
86-
val thumbUrl = config.cloudFrontDomainThumbBucket
87-
.flatMap(s3Client.signedCloudFrontUrl(_, fileUri.getPath.drop(1)))
88-
.getOrElse(s3SignedThumbUrl)
82+
val thumbUrl: String = getSecureThumbUrl(image)
8983

9084
val validityMap = checkUsageRestrictions(source, ImageExtras.validityMap(image, withWritePermission))
9185
val valid = ImageExtras.isValid(validityMap)
@@ -130,6 +124,16 @@ class ImageResponse(config: MediaApiConfig, s3Client: S3Client, usageQuota: Usag
130124
(data, links, actions)
131125
}
132126

127+
def getSecureThumbUrl(image: Image) = {
128+
val fileUri: URI = image.source.file
129+
130+
def s3SignedThumbUrl = s3Client.signUrl(config.thumbBucket, fileUri, image, imageType = Thumbnail)
131+
132+
config.cloudFrontDomainThumbBucket
133+
.flatMap(s3Client.signedCloudFrontUrl(_, fileUri.getPath.drop(1)))
134+
.getOrElse(s3SignedThumbUrl)
135+
}
136+
133137
private def downloadLink(id: String) = Link("download", s"${config.rootUri}/images/$id/download")
134138
private def downloadOptimisedLink(id: String) = Link("downloadOptimised", s"${config.rootUri}/images/$id/downloadOptimised?{&width,height,quality}")
135139

media-api/app/lib/elasticsearch/ElasticSearch.scala

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ import scalaz.NonEmptyList
2727
import scalaz.syntax.std.list._
2828

2929
import java.util.concurrent.TimeUnit
30-
import scala.concurrent.duration.FiniteDuration
31-
import scala.concurrent.{ExecutionContext, Future}
30+
import scala.concurrent.duration.{DurationInt, FiniteDuration}
31+
import scala.concurrent.{Await, ExecutionContext, Future, TimeoutException}
3232

3333
class ElasticSearch(
3434
val config: MediaApiConfig,
@@ -280,6 +280,21 @@ class ElasticSearch(
280280
}
281281
}
282282

283+
def getRelationDetails(mediaIdThisIsFor: String, getSecureThumbUrl: Image => String)(
284+
id: String
285+
)(implicit ex: ExecutionContext, request: AuthenticatedRequest[AnyContent, Principal], logMarker:MarkerMap = MarkerMap()): (String, Option[Map[String, String]]) = {
286+
try {
287+
id -> Await.result(getImageById(id), 5.seconds).map(image => Map(
288+
"thumbnail" -> getSecureThumbUrl(image),
289+
"addedBy" -> image.uploadedBy
290+
))
291+
} catch {
292+
case e: TimeoutException =>
293+
logger.error(logMarker, s"Timeout getting image $id (when finding relation details for $mediaIdThisIsFor)", e)
294+
id -> None
295+
}
296+
}
297+
283298
def usageForSupplier(id: String, numDays: Int)(implicit ex: ExecutionContext, logMarker: LogMarker): Future[SupplierUsageSummary] = {
284299
val supplier = Agencies.get(id)
285300
val supplierName = supplier.supplier

0 commit comments

Comments
 (0)