diff --git a/common/app/model/dotcomrendering/ElementsEnhancer.scala b/common/app/model/dotcomrendering/ElementsEnhancer.scala index 6821bc60cfc..e303f3b2953 100644 --- a/common/app/model/dotcomrendering/ElementsEnhancer.scala +++ b/common/app/model/dotcomrendering/ElementsEnhancer.scala @@ -2,34 +2,55 @@ package model.dotcomrendering import play.api.libs.json.{Json, _} -object ElementsEnhancer { +/** + * The `ElementsEnhancer` object provides functions to enhance JSON representations of elements used in Dotcom Rendering. + * It adds unique identifiers to elements, as expected by the DCR schemas. More information on the decision for this can be found in PageElement-Identifiers.md or by searching for "03feb394-a17d-4430-8384-edd1891e0d01" + * + */ - // Note: - // In the file PageElement-Identifiers.md you will find a discussion of identifiers used by PageElements - // Also look for "03feb394-a17d-4430-8384-edd1891e0d01" +object ElementsEnhancer { def enhanceElement(element: JsValue): JsValue = { + // Add an elementId to the element val elementWithId = element.as[JsObject] ++ Json.obj("elementId" -> java.util.UUID.randomUUID.toString) + // Extract element type val elementType = elementWithId.value("_type").as[String] - val elementIsList = elementType == "model.dotcomrendering.pageElements.ListBlockElement" - if (elementIsList) { - val listItems = elementWithId.value("items").as[JsArray] - val listItemsWithIds = listItems.value.map { item => - val obj = item.as[JsObject] - obj ++ Json.obj("elements" -> enhanceElements(obj.value("elements"))) + // If element has further nesting, continue to enhance. otherwise, return the enhanced element + elementType match { + case "model.dotcomrendering.pageElements.ListBlockElement" => enhanceListBlockElement(elementWithId) + case "model.dotcomrendering.pageElements.TimelineBlockElement" => enhanceTimelineBlockElement(elementWithId) + case _ => elementWithId; + } + + } + def enhanceListBlockElement(elementWithId: JsObject): JsObject = { + val listItems = elementWithId.value("items").as[JsArray] + val listItemsWithIds = listItems.value.map { item => + val obj = item.as[JsObject] + obj ++ Json.obj("elements" -> enhanceElements(obj.value("elements"))) + } + elementWithId ++ Json.obj("items" -> listItemsWithIds) + } + + def enhanceTimelineBlockElement(element: JsObject): JsObject = { + val sectionsList = element.value("sections").as[List[JsObject]] + val sectionsListWithIds = sectionsList.map { section => + val eventsList = section.value("events").as[List[JsObject]] + val eventsListWithIds = eventsList.map { event => + val bodyElementsWithIds = enhanceElements(event.value("body").as[JsArray]) + event.as[JsObject] ++ Json.obj("body" -> bodyElementsWithIds) } - elementWithId ++ Json.obj("items" -> listItemsWithIds) - } else { - elementWithId + section.as[JsObject] ++ Json.obj("events" -> eventsListWithIds) } + element ++ Json.obj("sections" -> sectionsListWithIds) } def enhanceElements(elements: JsValue): IndexedSeq[JsValue] = { elements.as[JsArray].value.map(element => enhanceElement(element)) }.toIndexedSeq - def enhanceObjectWithElementsAtDepth1(obj: JsValue): JsValue = { + def enhanceObjectWithElements(obj: JsValue): JsValue = { obj.asOpt[JsObject] match { case Some(o) => val elements = o.value("elements") @@ -38,21 +59,21 @@ object ElementsEnhancer { } } - def enhanceObjectsWithElementsAtDepth1(objs: JsValue): IndexedSeq[JsValue] = { - objs.as[JsArray].value.map(obj => enhanceObjectWithElementsAtDepth1(obj)) + def enhanceObjectsWithElements(objs: JsValue): IndexedSeq[JsValue] = { + objs.as[JsArray].value.map(obj => enhanceObjectWithElements(obj)) }.toIndexedSeq def enhanceBlocks(obj: JsObject): JsObject = { obj ++ - Json.obj("blocks" -> enhanceObjectsWithElementsAtDepth1(obj.value("blocks"))) + Json.obj("blocks" -> enhanceObjectsWithElements(obj.value("blocks"))) } def enhanceDcrObject(obj: JsObject): JsObject = { obj ++ - Json.obj("blocks" -> enhanceObjectsWithElementsAtDepth1(obj.value("blocks"))) ++ + Json.obj("blocks" -> enhanceObjectsWithElements(obj.value("blocks"))) ++ Json.obj("mainMediaElements" -> enhanceElements(obj.value("mainMediaElements"))) ++ - Json.obj("keyEvents" -> enhanceObjectsWithElementsAtDepth1(obj.value("keyEvents"))) ++ - Json.obj("pinnedPost" -> enhanceObjectWithElementsAtDepth1(obj.value("pinnedPost"))) ++ + Json.obj("keyEvents" -> enhanceObjectsWithElements(obj.value("keyEvents"))) ++ + Json.obj("pinnedPost" -> enhanceObjectWithElements(obj.value("pinnedPost"))) ++ Json.obj("promotedNewsletter" -> obj.value("promotedNewsletter")) } } diff --git a/common/app/model/dotcomrendering/pageElements/PageElement.scala b/common/app/model/dotcomrendering/pageElements/PageElement.scala index ee8c5d7ec7c..be45f3b6e79 100644 --- a/common/app/model/dotcomrendering/pageElements/PageElement.scala +++ b/common/app/model/dotcomrendering/pageElements/PageElement.scala @@ -877,6 +877,8 @@ object PageElement { case _: WitnessBlockElement => true case _: VineBlockElement => true case _: ListBlockElement => true + case _: TimelineBlockElement => true + // TODO we should quick fail here for these rather than pointlessly go to DCR case table: TableBlockElement if table.isMandatory.exists(identity) => true