Skip to content

Commit 6171958

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 f7d86a8 commit 6171958

File tree

3 files changed

+45
-12
lines changed

3 files changed

+45
-12
lines changed

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, _}
@@ -14,6 +15,7 @@ import com.gu.mediaservice.lib.formatting.printDateTime
1415
import com.gu.mediaservice.lib.logging.MarkerMap
1516
import com.gu.mediaservice.lib.metadata.SoftDeletedMetadataTable
1617
import com.gu.mediaservice.model._
18+
import com.gu.mediaservice.model.usage.{DerivativeUsageStatus, ReplacedUsageStatus}
1719
import com.gu.mediaservice.syntax.MessageSubjects
1820
import lib._
1921
import lib.elasticsearch._
@@ -494,6 +496,14 @@ class MediaApi(
494496
val deleteImagePermission = authorisation.isUploaderOrHasPermission(request.user, source.instance.uploadedBy, DeleteImagePermission)
495497
val deleteCropsOrUsagePermission = canUserDeleteCropsOrUsages(request.user)
496498

499+
val getRelationDetails = elasticSearch.getRelationDetails(id, imageResponse.getSecureThumbUrl)_
500+
val relationDetails = Map(
501+
"Replacement for" -> source.instance.identifiers.get(ImageStorageProps.replacesMediaIdIdentifierKey).map(getRelationDetails),
502+
"Replaced by" -> source.instance.usages.filter(_.status == ReplacedUsageStatus).flatMap(_.childUsageMetadata.map(_.childMediaId)).map(getRelationDetails),
503+
"Derivative of" -> source.instance.identifiers.get(ImageStorageProps.derivativeOfMediaIdsIdentifierKey).toList.flatMap(_.split(",").map(_.trim)).map(getRelationDetails),
504+
"Derivatives" -> source.instance.usages.filter(_.status == DerivativeUsageStatus).flatMap(_.childUsageMetadata.map(_.childMediaId)).map(getRelationDetails)
505+
).view.mapValues(_.iterator.toMap).toMap
506+
497507
val (imageData, imageLinks, imageActions) = imageResponse.create(
498508
id,
499509
source,
@@ -504,8 +514,12 @@ class MediaApi(
504514
request.user.accessor.tier
505515
)
506516

507-
Some((source.instance, imageData, imageLinks, imageActions))
508-
517+
Some((
518+
source.instance,
519+
imageData.asInstanceOf[JsObject] + ("parentAndChildDetails" -> Json.toJson(relationDetails)),
520+
imageLinks,
521+
imageActions
522+
))
509523
case _ => None
510524
}
511525
}

media-api/app/lib/ImageResponse.scala

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

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

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

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

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

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

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,
@@ -278,6 +278,21 @@ class ElasticSearch(
278278
}
279279
}
280280

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

0 commit comments

Comments
 (0)