Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Feast model to draw data from new card meta #1612

Merged
merged 2 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions app/model/FeastAppModel.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,31 @@ import model.editions.Edition
import play.api.libs.json._

import java.time.LocalDate
import model.editions.Palette

object FeastAppModel {
sealed trait ContainerItem

case class RecipeIdentifier(id:String)
case class Recipe(recipe:RecipeIdentifier) extends ContainerItem
case class Chef(id:String, image:Option[String], bio: Option[String], backgroundHex:Option[String], foregroundHex:Option[String]) extends ContainerItem
case class Palette(backgroundHex:String, foregroundHex:String)
case class FeastCollection(byline:Option[String], darkPalette:Option[Palette], image:Option[String], body:Option[String], title:String, lightPalette:Option[Palette], recipes:Seq[String]) extends ContainerItem
case class RecipeIdentifier(id: String)

case class Recipe(recipe: RecipeIdentifier) extends ContainerItem

case class Chef(id: String,
image: Option[String] = None,
bio: Option[String] = None,
backgroundHex: Option[String] = None,
foregroundHex: Option[String] = None
) extends ContainerItem

case class FeastCollection(
byline: Option[String] = None,
darkPalette: Option[Palette] = None,
image: Option[String] = None,
body: Option[String] = None,
title: String,
lightPalette: Option[Palette] = None,
recipes: Seq[String]
) extends ContainerItem

case class FeastAppContainer(id:String, title:String, body:Option[String], items:Seq[ContainerItem])
//type FeastAppCuration = Map[String, IndexedSeq[FeastAppContainer]]
Expand Down
27 changes: 22 additions & 5 deletions app/services/editions/publishing/FeastPublicationTarget.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import play.api.libs.json.{Json, Writes}
import util.TimestampGenerator

import scala.jdk.CollectionConverters._
import logging.Logging

object FeastPublicationTarget {
object MessageType extends Enumeration {
Expand All @@ -19,17 +20,30 @@ object FeastPublicationTarget {
type MessageType = MessageType.Value
}

class FeastPublicationTarget(snsClient: AmazonSNS, config: ApplicationConfiguration, timestamp: TimestampGenerator) extends PublicationTarget {
private def transformArticles(source: EditionsCard): ContainerItem = {
class FeastPublicationTarget(snsClient: AmazonSNS, config: ApplicationConfiguration, timestamp: TimestampGenerator) extends PublicationTarget with Logging {
private def transformCards(source: EditionsCard): ContainerItem = {
source match {
case _: EditionsArticle => throw new Error("Article not permitted in a Feast Front")
case EditionsRecipe(id, _) => Recipe(RecipeIdentifier(id))
case EditionsChef(id, addedOn, metadata) => Chef(id = id,
case EditionsChef(id, _, metadata) => Chef(id = id,
image = metadata.flatMap(_.chefImageOverride.map(_.src)),
bio = metadata.flatMap(_.bio),
backgroundHex = metadata.flatMap(_.theme.map(_.palette.backgroundHex)),
foregroundHex = metadata.flatMap(_.theme.map(_.palette.foregroundHex)))
case _:EditionsFeastCollection => FeastCollection(byline=None, darkPalette=None, image=None, body=None, title="", lightPalette=None, recipes=List.empty)
case EditionsFeastCollection(_, _, metadata) =>
val recipes = metadata.map(_.collectionItems.map {
case EditionsRecipe(id, _) => id
}).getOrElse(List.empty)

FeastCollection(
byline = None,
darkPalette = metadata.flatMap(_.theme.map(_.darkPalette)),
lightPalette = metadata.flatMap(_.theme.map(_.lightPalette)),
image = metadata.flatMap(_.theme.flatMap(_.imageURL)),
body = None,
title = metadata.flatMap(_.title).getOrElse("No title"),
recipes = recipes
)
}
}

Expand All @@ -48,7 +62,7 @@ class FeastPublicationTarget(snsClient: AmazonSNS, config: ApplicationConfigurat
id = collection.id,
title = collection.displayName,
body = Some(""), //TBD, this is just how it appears in the data at the moment
items = collection.items.map(transformArticles)
items = collection.items.map(transformCards)
)

def transformContent(source: EditionsIssue, version: String): FeastAppCuration = {
Expand Down Expand Up @@ -80,6 +94,9 @@ class FeastPublicationTarget(snsClient: AmazonSNS, config: ApplicationConfigurat

override def putIssueJson[T: Writes](issue: T, key: String): Unit = {
val content = Json.stringify(Json.toJson(issue))

logger.info(s"Publishing content for issue $key: $content")

snsClient.publish(createPublishRequest(content, FeastPublicationTarget.MessageType.Issue))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,27 @@ package services.editions.publishing
import com.amazonaws.services.sns.AmazonSNSClient
import com.amazonaws.services.sns.model.{MessageAttributeValue, PublishRequest, PublishResult}
import conf.ApplicationConfiguration
import model.FeastAppModel
import model.editions.{CuratedPlatform, Edition, EditionsCollection, EditionsFront, EditionsIssue, EditionsRecipe, PublishAction}
import org.mockito.Mockito._
import org.mockito.ArgumentMatchers._
import org.scalatest.{FreeSpec, Matchers}
import org.scalatestplus.mockito.MockitoSugar
import play.api.Configuration
import play.api.libs.json.Json
import model.FeastAppModel.{Chef, FeastAppContainer, Recipe, RecipeIdentifier}
import model.FeastAppModel.{Chef, FeastCollection, FeastAppContainer, Recipe, RecipeIdentifier}
import util.TimestampGenerator

import java.time.LocalDate
import scala.jdk.CollectionConverters._
import scala.util.{Failure, Try}
import model.editions.EditionsFeastCollection
import model.editions.EditionsFeastCollectionMetadata
import model.editions.FeastCollectionTheme
import model.editions.Palette
import model.editions.EditionsChef
import model.editions.EditionsChefMetadata
import model.editions.ChefTheme
import model.editions.Image

class FeastPublicationTargetTest extends FreeSpec with Matchers with MockitoSugar {
val conf = new ApplicationConfiguration(
Expand Down Expand Up @@ -63,8 +70,40 @@ class FeastPublicationTargetTest extends FreeSpec with Matchers with MockitoSuga
contentPrefillTimeWindow = None,
items=List(
EditionsRecipe(
"id",
"recipe-id",
0L
),
EditionsChef(
"chef-id",
0L,
Some(EditionsChefMetadata(
bio = Some("bio"),
theme = Some(ChefTheme(
id = "theme-id",
palette = Palette("#FFF", "#333"),
)),
chefImageOverride = Some(Image(
width = None,
height = None,
origin = "image-origin",
src = "image-src"
))
))
),
EditionsFeastCollection(
"collection-id",
0L,
Some(EditionsFeastCollectionMetadata(
title = Some("Collection title"),
theme = Some(FeastCollectionTheme(
id = "theme-id",
lightPalette = Palette("#FFF", "#333"),
darkPalette = Palette("#333", "#FFF"),
imageURL = Some("https://example.com/an-image.jpg")
)),
collectionItems = List(EditionsRecipe("nested-recipe-id", 0L))

))
)
)
)
Expand All @@ -82,7 +121,7 @@ class FeastPublicationTargetTest extends FreeSpec with Matchers with MockitoSuga
"recipes" -> FeastAppContainer("recipes", "Recipes", None, Seq(Recipe(RecipeIdentifier("abcdefg"))))
)

"should push the relevant content into SNS" - {
"should push the relevant content into SNS" in {
val mockSNS = mock[AmazonSNSClient]
when(mockSNS.publish(any[PublishRequest])).thenReturn(new PublishResult())

Expand All @@ -101,7 +140,7 @@ class FeastPublicationTargetTest extends FreeSpec with Matchers with MockitoSuga
verify(mockSNS, times(1)).publish(expectedRequest)
}

"should not catch an SNS exception" - {
"should not catch an SNS exception" in {
val mockSNS = mock[AmazonSNSClient]
val except = new RuntimeException("My hovercraft is full of eels")
when(mockSNS.publish(any[PublishRequest])).thenThrow(except)
Expand All @@ -114,7 +153,7 @@ class FeastPublicationTargetTest extends FreeSpec with Matchers with MockitoSuga
}

"putEditionsList" - {
"should push the relevant content into SNS" - {
"should push the relevant content into SNS" in {
val mockSNS = mock[AmazonSNSClient]
when(mockSNS.publish(any[PublishRequest])).thenReturn(new PublishResult())

Expand All @@ -135,7 +174,7 @@ class FeastPublicationTargetTest extends FreeSpec with Matchers with MockitoSuga
}

"transformContent" - {
"should transform the Editions content" - {
"should transform the Editions content" in {
val mockSNS = mock[AmazonSNSClient]
when(mockSNS.publish(any[PublishRequest])).thenReturn(new PublishResult())

Expand All @@ -148,14 +187,29 @@ class FeastPublicationTargetTest extends FreeSpec with Matchers with MockitoSuga
allRecipesFront.head.title shouldBe "Dish of the day"
allRecipesFront.head.body shouldBe Some("") //this is just how the `body` field is currently rendered
allRecipesFront.head.id shouldBe "98e89761-fdf0-4903-b49d-2af7d66fc930"
allRecipesFront.head.items.length shouldBe 1
allRecipesFront.head.items.head.asInstanceOf[FeastAppModel.Recipe].recipe.id shouldBe "id"
allRecipesFront.head.items shouldBe List(
Recipe(RecipeIdentifier("recipe-id")),
Chef(
id = "chef-id",
image = Some("image-src"),
bio = Some("bio"),
backgroundHex = Some("#333"),
foregroundHex = Some("#FFF")
),
FeastCollection(
darkPalette = Some(Palette("#333", "#FFF")),
lightPalette = Some(Palette("#FFF", "#333")),
image = Some("https://example.com/an-image.jpg"),
title = "Collection title",
recipes = List("nested-recipe-id")
)
)
}
}

"putIssue" - {
"should output the transformed version of the content" - {
val serializedVersion = """{"id":"123456ABCD","edition":"feast-northern-hemisphere","issueDate":"2024-05-03","version":"v1","fronts":{"all-recipes":[{"id":"98e89761-fdf0-4903-b49d-2af7d66fc930","title":"Dish of the day","body":"","items":[{"recipe":{"id":"id"}}]}]}}"""
"should output the transformed version of the content" in {
val serializedVersion = """{"id":"123456ABCD","edition":"feast-northern-hemisphere","issueDate":"2024-05-03","version":"v1","fronts":{"all-recipes":[{"id":"98e89761-fdf0-4903-b49d-2af7d66fc930","title":"Dish of the day","body":"","items":[{"recipe":{"id":"recipe-id"}},{"id":"chef-id","image":"image-src","bio":"bio","backgroundHex":"#333","foregroundHex":"#FFF"},{"darkPalette":{"foregroundHex":"#333","backgroundHex":"#FFF"},"image":"https://example.com/an-image.jpg","title":"Collection title","lightPalette":{"foregroundHex":"#FFF","backgroundHex":"#333"},"recipes":["nested-recipe-id"]}]}]}}"""

val mockSNS = mock[AmazonSNSClient]
when(mockSNS.publish(any[PublishRequest])).thenReturn(new PublishResult())
Expand Down
Loading