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

fetch newsletter layouts #27587

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
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
19 changes: 16 additions & 3 deletions applications/app/controllers/SignupPageController.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package controllers

import common.{GuLogging, ImplicitControllerExecutionContext}
import common.{GuLogging, ImplicitControllerExecutionContext, Edition}
import conf.switches.Switches.UseDcrNewslettersPage
import model.{ApplicationContext, Cached, NoCache}
import model.Cached.RevalidatableResult
Expand All @@ -11,7 +11,7 @@ import play.filters.csrf.CSRFAddToken
import renderers.DotcomRenderingService
import services.newsletters.GroupedNewslettersResponse.GroupedNewslettersResponse
import services.newsletters.NewsletterSignupAgent
import services.newsletters.model.NewsletterResponseV2
import services.newsletters.model.{NewsletterResponseV2, NewsletterLayoutGroup}
import staticpages.StaticPages
import implicits.{HtmlFormat, JsonFormat}
import implicits.Requests.RichRequestHeader
Expand Down Expand Up @@ -61,12 +61,14 @@ class SignupPageController(

val newsletters: Either[String, List[NewsletterResponseV2]] =
newsletterSignupAgent.getV2Newsletters()
val layout = getLayout()

newsletters match {
case Right(newsletters) =>
remoteRenderer.getEmailNewsletters(
ws = wsClient,
newsletters = newsletters,
layout = layout,
page = StaticPages.dcrSimpleNewsletterPage(request.path),
)
case Left(e) =>
Expand All @@ -84,8 +86,9 @@ class SignupPageController(
newsletters match {
case Right(newsletters) => {
val page = StaticPages.dcrSimpleNewsletterPage(request.path)
val layout = getLayout()
val dataModel =
DotcomNewslettersPageRenderingDataModel.apply(page, newsletters, request)
DotcomNewslettersPageRenderingDataModel.apply(page, newsletters, layout, request)
val dataJson = DotcomNewslettersPageRenderingDataModel.toJson(dataModel)
Future.successful(common.renderJson(dataJson, page).as("application/json"))
}
Expand All @@ -95,6 +98,16 @@ class SignupPageController(
}
}

private def getLayout()(implicit
request: RequestHeader,
): Option[List[NewsletterLayoutGroup]] = {
val layouts: Map[String, List[NewsletterLayoutGroup]] = newsletterSignupAgent.getNewsletterLayouts() match {
case Right(layoutsMap) => layoutsMap
case Left(_) => Map.empty
}
layouts.get(Edition.edition(request).id)
}

private def notFoundPage()(implicit
request: RequestHeader,
): Future[Result] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ import navigation.{FooterLinks, Nav}
import play.api.libs.json.{JsObject, JsValue, Json, OWrites}
import play.api.mvc.RequestHeader
import views.support.{CamelCase, JavaScriptPage}
import services.newsletters.model.NewsletterResponseV2
import services.newsletters.model.{NewsletterResponseV2, NewsletterLayoutGroup}
import services.NewsletterData

case class DotcomNewslettersPageRenderingDataModel(
newsletters: List[NewsletterData],
layout: Option[List[NewsletterLayoutGroup]],
id: String,
editionId: String,
editionLongForm: String,
Expand All @@ -42,6 +43,7 @@ object DotcomNewslettersPageRenderingDataModel {
def apply(
page: SimplePage,
newsletters: List[NewsletterResponseV2],
layout: Option[List[NewsletterLayoutGroup]],
request: RequestHeader,
): DotcomNewslettersPageRenderingDataModel = {
val edition = Edition.edition(request)
Expand Down Expand Up @@ -78,6 +80,7 @@ object DotcomNewslettersPageRenderingDataModel {

DotcomNewslettersPageRenderingDataModel(
newsletters = newsletterData,
layout = layout,
id = page.metadata.id,
editionId = edition.id,
editionLongForm = edition.displayName,
Expand Down
5 changes: 3 additions & 2 deletions common/app/renderers/DotcomRenderingService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import play.api.libs.ws.{WSClient, WSResponse}
import play.api.mvc.Results.{InternalServerError, NotFound}
import play.api.mvc.{RequestHeader, Result}
import play.twirl.api.Html
import services.newsletters.model.NewsletterResponseV2
import services.newsletters.model.{NewsletterResponseV2, NewsletterLayoutGroup}
import services.{IndexPage, NewsletterData}

import java.lang.System.currentTimeMillis
Expand Down Expand Up @@ -342,10 +342,11 @@ class DotcomRenderingService extends GuLogging with ResultWithPreconnectPreload
def getEmailNewsletters(
ws: WSClient,
newsletters: List[NewsletterResponseV2],
layout: Option[List[NewsletterLayoutGroup]],
page: SimplePage,
)(implicit request: RequestHeader): Future[Result] = {

val dataModel = DotcomNewslettersPageRenderingDataModel.apply(page, newsletters, request)
val dataModel = DotcomNewslettersPageRenderingDataModel.apply(page, newsletters, layout, request)
val json = DotcomNewslettersPageRenderingDataModel.toJson(dataModel)
post(ws, json, Configuration.rendering.faciaBaseURL + "/EmailNewsletters", CacheTime.Facia)
}
Expand Down
18 changes: 17 additions & 1 deletion common/app/services/newsletters/NewsletterApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ import common.{BadConfigurationException, GuLogging}
import conf.Configuration._
import play.api.libs.json.{JsError, JsSuccess, JsValue}
import play.api.libs.ws.WSClient
import services.newsletters.model.{NewsletterResponse, NewsletterResponseV2, NewslettersGetResponseV2Body}
import services.newsletters.model.{
NewsletterResponse,
NewsletterResponseV2,
NewslettersGetResponseV2Body,
NewsletterLayoutsResponseBody,
NewsletterLayoutGroup,
}

import scala.concurrent.duration.DurationInt
import scala.concurrent.{ExecutionContext, Future}
Expand Down Expand Up @@ -40,6 +46,16 @@ case class NewsletterApi(wsClient: WSClient)(implicit executionContext: Executio
}
}

def getNewsletterLayouts(): Future[Either[String, Map[String, List[NewsletterLayoutGroup]]]] = {
getBody("api/layouts").map { json =>
json.validate[NewsletterLayoutsResponseBody] match {
case succ: JsSuccess[NewsletterLayoutsResponseBody] =>
Right(succ.get.data)
case err: JsError => Left(err.toString)
}
}
}

private def getBody(path: String): Future[JsValue] = {
val maybeJson: Option[Future[JsValue]] = for {
host <- newsletterApi.host
Expand Down
14 changes: 13 additions & 1 deletion common/app/services/newsletters/NewsletterSignupAgent.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package services.newsletters

import common.{Box, GuLogging}
import services.newsletters.GroupedNewslettersResponse.GroupedNewslettersResponse
import services.newsletters.model.{NewsletterResponse, NewsletterResponseV2}
import services.newsletters.model.{NewsletterResponse, NewsletterResponseV2, NewsletterLayoutGroup}

import scala.concurrent.{ExecutionContext, Future}

Expand All @@ -14,6 +14,8 @@ class NewsletterSignupAgent(newsletterApi: NewsletterApi) extends GuLogging {
private val groupedNewslettersAgent = Box[Either[String, GroupedNewslettersResponse]](Right(List.empty))
// Newsletters version 2
private val newslettersV2Agent = Box[Either[String, List[NewsletterResponseV2]]](Right(Nil))
// Newsletter layouts
private val newsletterLayoutsAgent = Box[Either[String, Map[String, List[NewsletterLayoutGroup]]]](Right(Map.empty))

def getNewsletterByName(listName: String): Either[String, Option[NewsletterResponse]] = {
newslettersAgent.get() match {
Expand Down Expand Up @@ -55,6 +57,8 @@ class NewsletterSignupAgent(newsletterApi: NewsletterApi) extends GuLogging {

def getV2Newsletters(): Either[String, List[NewsletterResponseV2]] = newslettersV2Agent.get()

def getNewsletterLayouts(): Either[String, Map[String, List[NewsletterLayoutGroup]]] = newsletterLayoutsAgent.get()

def refresh()(implicit ec: ExecutionContext): Future[Unit] = {
refreshNewsletters() recover { case e =>
val errMessage = s"Call to Newsletter API failed: ${e.getMessage}"
Expand Down Expand Up @@ -84,6 +88,14 @@ class NewsletterSignupAgent(newsletterApi: NewsletterApi) extends GuLogging {
case Left(err) =>
log.error(s"Failed to refresh v2 Newsletters cache: $err")
}

newsletterApi.getNewsletterLayouts() map {
case Right(layoutsMap) =>
newsletterLayoutsAgent.alter(Right(layoutsMap))
log.info("Successfully refreshed newsletters layouts cache.")
case Left(err) =>
log.error(s"Failed to refresh newsletters layouts cache: $err")
}
}

private def buildGroupedNewsletters(newsletters: List[NewsletterResponse]): GroupedNewslettersResponse = {
Expand Down
26 changes: 26 additions & 0 deletions common/app/services/newsletters/model/NewsletterResponse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,29 @@ object NewslettersGetResponseV2Body {
implicit val newslettersGetResponseV2BodyWrites: OWrites[NewslettersGetResponseV2Body] =
Json.writes[NewslettersGetResponseV2Body]
}

case class NewsletterLayoutGroup(
title: String,
subtitle: Option[String],
newsletters: List[String],
)

object NewsletterLayoutGroup {
implicit val newsletterLayoutGroupReads: Reads[NewsletterLayoutGroup] =
Json.reads[NewsletterLayoutGroup]
implicit val newsletterLayoutGroupWrites: OWrites[NewsletterLayoutGroup] =
Json.writes[NewsletterLayoutGroup]
}

case class NewsletterLayoutsResponseBody(
ok: Boolean,
total: Int,
data: Map[String, List[NewsletterLayoutGroup]],
)

object NewsletterLayoutsResponseBody {
implicit val newsletterLayoutsResponseBodyReads: Reads[NewsletterLayoutsResponseBody] =
Json.reads[NewsletterLayoutsResponseBody]
implicit val newsletterLayoutsResponseBodyWrites: OWrites[NewsletterLayoutsResponseBody] =
Json.writes[NewsletterLayoutsResponseBody]
}