Add RecipeBlockElement and related case classes for recipe data handling#28759
Open
andresilva-guardian wants to merge 21 commits into
Open
Add RecipeBlockElement and related case classes for recipe data handling#28759andresilva-guardian wants to merge 21 commits into
andresilva-guardian wants to merge 21 commits into
Conversation
Contributor
…es field from linkedData
…n/frontend into afs/recipe-block-element
Contributor
|
The PR appears to have expected code for the Recipe element to flow to the frontend. I have a few questions to clarify:
|
Divs-B
reviewed
May 28, 2026
| implicit val ProductBlockElementWrites: Writes[ProductBlockElement] = Json.writes[ProductBlockElement] | ||
| } | ||
|
|
||
| case class RecipeFeaturedImage( |
Contributor
There was a problem hiding this comment.
I am curious to check if there is reason behind these mandatory 3 fields only for available featuredImage? Are we having same schema in CAPI?
Divs-B
reviewed
May 28, 2026
| // NOTE: This case class intentionally exposes only a subset of the full recipe schema. | ||
| // If you need to add more fields, refer to the canonical spec and extend accordingly: | ||
| // https://github.com/guardian/recipes-backend/blob/main/schema/recipe-v3.json | ||
| case class RecipeBlockElement( |
Contributor
There was a problem hiding this comment.
It appears that we are only seeking id and others are Optional. I trust this aligns with the requirement. However, for the sake of maintaining a minimal stack of knowledge for a recipe block element, could we also retain datePublished and author?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What is the value of this and can you measure success?
Recipe articles (e.g.
food/2026/apr/27/pasta-bake-sumac-salad-prep-ahead-recipes-sami-tamimi) contain structured recipe data authored in Composer. This data is stored in CAPI asElementType.RECIPEbody block elements with arecipeTypeData.recipeJsonblob, but we were silently discarding it, meaning DCR never received it.We are making that structured data available to DCR, which is a prerequisite for rendering interactive recipe cards (ingredients, steps, timings, image, deep-links into the Feast app) inline in food articles.
We can measure success by verifying that
RecipeBlockElementnodes are present in theFEArticleJSON payload for recipe articles (e.g. via the?dcr=truedebug view).What does this change?
We only changed a single file: PageElement.scala.
New data model
We introduced two new case classes:
RecipeFeaturedImage, the recipe hero image, with required fieldsurl,mediaId,cropId, and optional fieldssource,photographer,caption,imageType,width,height,mediaApiUri.RecipeBlockElement, the top-level element extendingPageElement, with a requiredidand optionaltitle,description,featuredImage. Theidis the stable recipe identifier used for Feast deep-links.The model is intentionally minimal, exposing only the subset of fields DCR currently needs. If we need more fields, we should refer to the canonical schema at
guardian/recipes-backend/schema/recipe-v3.json.Data flow
We added a
case Recipe =>handler inPageElement.make(), which was previously missing (falling through tocase _ => Nil). TherecipeJsonstring from CAPI is parsed using Play JSONReadsderivation and mapped to aRecipeBlockElement.flowchart LR A["CAPI\n(ElementType.RECIPE)"] --> B["recipeTypeData\n.recipeJson\n(raw JSON string)"] B --> C["Json.parse(json)"] C --> D["json.validate\n[RecipeBlockElement]"] D --> E["RecipeBlockElement\n(id, title, description,\nfeaturedImage)"] E --> F["FEArticle body blocks\n→ DCR"]We also added
RecipeBlockElementtoisSupportedso it passes the element filter and is included in the DCR payload.Feast visibility
Feast visibility is determined entirely by the presence of a
RecipeBlockElement. Any article with a recipe element is visible in Feast unless explicitly blocked via channels. There is no per-recipe flag for this.Deployment order, DCR must ship first
DCR validates all incoming
FEArticlepayloads against an AJV JSON Schema (feArticle.json). The schema usesadditionalProperties: false-style validation with explicitanyOftype discrimination, meaning any_typevalue not listed in the schema causes a hard validation error and returns a 500 for that article.We must not merge this PR to
mainuntil the corresponding DCR PR (which addsRecipeBlockElementtofeArticle.jsonand implements the recipe card renderer) has been deployed to PROD. The safe deployment order is:flowchart TD A["1. Merge DCR PR\n(adds RecipeBlockElement to feArticle.json\n+ recipe card renderer)"] --> B["2. Deploy DCR PR to PROD"] B --> C["3. Merge this frontend PR\n(safe to merge now)"] C --> D["4. Deploy this frontend PR to PROD"]Screenshots
No visual change on the frontend, this is a data pipeline change. The structured recipe data will be visible in the DCR payload (
?dcr=true) for recipe articles once deployed.Checklist
data/databasefiles generated by tests are committed with this PR (the tests will fail in CI if you've forgotten to do this)