diff --git a/applications/app/controllers/CrosswordsController.scala b/applications/app/controllers/CrosswordsController.scala index 41e2757662d..48c16256bec 100644 --- a/applications/app/controllers/CrosswordsController.scala +++ b/applications/app/controllers/CrosswordsController.scala @@ -1,10 +1,10 @@ package controllers +import com.gu.contentapi.client.model.v1.CrosswordType.{Cryptic, Quick} import com.gu.contentapi.client.model.v1.{Crossword, ItemResponse, Content => ApiContent, Section => ApiSection} import common.{Edition, GuLogging, ImplicitControllerExecutionContext} import conf.Static import contentapi.ContentApiClient -import pages.{CrosswordHtmlPage, IndexHtmlPage, PrintableCrosswordHtmlPage} import crosswords.{ AccessibleCrosswordPage, AccessibleCrosswordRows, @@ -14,18 +14,20 @@ import crosswords.{ CrosswordSearchPageWithResults, CrosswordSvg, } +import html.HtmlPageHelpers.ContentCSSFile import model.Cached.{RevalidatableResult, WithoutRevalidationResult} import model._ +import model.dotcomrendering.pageElements.EditionsCrosswordRenderingDataModel +import model.dotcomrendering.{DotcomRenderingDataModel, PageType} import org.joda.time.{DateTime, LocalDate} +import pages.{CrosswordHtmlPage, IndexHtmlPage, PrintableCrosswordHtmlPage} import play.api.data.Forms._ import play.api.data._ -import play.api.mvc.{Action, RequestHeader, Result, _} -import services.{IndexPage, IndexPageItem} -import html.HtmlPageHelpers.ContentCSSFile -import model.dotcomrendering.{DotcomRenderingDataModel, PageType} import play.api.libs.ws.WSClient +import play.api.mvc._ import renderers.DotcomRenderingService import services.dotcomrendering.{CrosswordsPicker, RemoteRender} +import services.{IndexPage, IndexPageItem} import scala.concurrent.Future import scala.concurrent.duration._ @@ -290,17 +292,33 @@ class CrosswordSearchController( case class CrosswordLookup(crosswordType: String, id: Int) } -class CrosswordEditionsController(val controllerComponents: ControllerComponents) - extends BaseController +class CrosswordEditionsController( + val contentApiClient: ContentApiClient, + val controllerComponents: ControllerComponents, + val remoteRenderer: DotcomRenderingService = DotcomRenderingService(), + val wsClient: WSClient, +) extends BaseController with GuLogging with ImplicitControllerExecutionContext { - def digitalEdition: Action[AnyContent] = - Action.async { implicit request => - Future.successful( - Cached(CacheTime.Default)( - RevalidatableResult.Ok("Digital Edition Crossword Entry Point"), - ), - ) - } + def digitalEdition: Action[AnyContent] = Action.async { implicit request => + getCrosswords + .map(parseCrosswords) + .flatMap { + case Some(crosswordPage) => + remoteRenderer.getEditionsCrossword(wsClient, crosswordPage) + case None => Future.successful(NotFound) + } + } + + private lazy val crosswordsQuery = contentApiClient.item("crosswords") + + private def getCrosswords: Future[ItemResponse] = contentApiClient.getResponse(crosswordsQuery) + + private def parseCrosswords(response: ItemResponse): Option[EditionsCrosswordRenderingDataModel] = + for { + results <- response.results + quick <- results.find(_.crossword.exists(_.`type` == Quick)).flatMap(_.crossword) + cryptic <- results.find(_.crossword.exists(_.`type` == Cryptic)).flatMap(_.crossword) + } yield EditionsCrosswordRenderingDataModel(quick, cryptic) } diff --git a/build.sbt b/build.sbt index 611392947a1..afc1bac4d01 100644 --- a/build.sbt +++ b/build.sbt @@ -31,6 +31,7 @@ val common = library("common") eTagCachingS3, nettyCodecHttp2, contentApiClient, + contentApiModelsJson, enumeratumPlayJson, filters, commonsLang, diff --git a/common/app/conf/switches/ABTestSwitches.scala b/common/app/conf/switches/ABTestSwitches.scala index 4242553405b..3a1af373203 100644 --- a/common/app/conf/switches/ABTestSwitches.scala +++ b/common/app/conf/switches/ABTestSwitches.scala @@ -55,7 +55,18 @@ trait ABTestSwitches { "Test the impact of showing the user a component that highlights the Guardians journalism", owners = Seq(Owner.withEmail("dotcom.platform@guardian.co.uk")), safeState = Off, - sellByDate = Some(LocalDate.of(2024, 12, 18)), + sellByDate = Some(LocalDate.of(2025, 1, 29)), + exposeClientSide = true, + highImpact = false, + ) + + Switch( + ABTests, + "ab-onwards-content-article", + "Test the impact of showing the galleries onwards content component on article pages", + owners = Seq(Owner.withEmail("dotcom.platform@guardian.co.uk")), + safeState = Off, + sellByDate = Some(LocalDate.of(2025, 1, 29)), exposeClientSide = true, highImpact = false, ) diff --git a/common/app/model/CollectionConfig.scala b/common/app/model/CollectionConfig.scala index d12150f9a9b..b00209afa24 100644 --- a/common/app/model/CollectionConfig.scala +++ b/common/app/model/CollectionConfig.scala @@ -23,6 +23,7 @@ final case class CollectionConfig( hideShowMore: Boolean, displayHints: Option[DisplayHints], platform: Option[CollectionPlatform] = None, + aspectRatio: Option[String], ) object CollectionConfig { @@ -53,6 +54,8 @@ object CollectionConfig { None } + val aspectRatio: String = fapi.CollectionConfig.getAspectRatio(config).label + CollectionConfig( displayName = config.displayName, backfill = config.backfill, @@ -73,6 +76,7 @@ object CollectionConfig { hideShowMore = config.hideShowMore, displayHints = config.displayHints.map(DisplayHints.make), platform = Some(config.platform), + aspectRatio = Some(aspectRatio), ) } diff --git a/common/app/model/dotcomrendering/pageElements/EditionsCrosswordRenderingDataModel.scala b/common/app/model/dotcomrendering/pageElements/EditionsCrosswordRenderingDataModel.scala new file mode 100644 index 00000000000..92cfff9ec8a --- /dev/null +++ b/common/app/model/dotcomrendering/pageElements/EditionsCrosswordRenderingDataModel.scala @@ -0,0 +1,20 @@ +package model.dotcomrendering.pageElements + +import com.gu.contentapi.client.model.v1.Crossword +import com.gu.contentapi.json.CirceEncoders._ +import io.circe.JsonObject +import io.circe.syntax._ + +case class EditionsCrosswordRenderingDataModel( + quick: Crossword, + cryptic: Crossword, +) + +object EditionsCrosswordRenderingDataModel { + def toJson(model: EditionsCrosswordRenderingDataModel): String = { + JsonObject( + "quick" -> model.quick.asJson.dropNullValues, + "cryptic" -> model.cryptic.asJson.dropNullValues, + ).asJson.dropNullValues.noSpaces + } +} diff --git a/common/app/renderers/DotcomRenderingService.scala b/common/app/renderers/DotcomRenderingService.scala index d9cf9b59e0a..32d7e067a59 100644 --- a/common/app/renderers/DotcomRenderingService.scala +++ b/common/app/renderers/DotcomRenderingService.scala @@ -1,7 +1,7 @@ package renderers import org.apache.pekko.actor.{ActorSystem => PekkoActorSystem} -import com.gu.contentapi.client.model.v1.{Block, Blocks, Content} +import com.gu.contentapi.client.model.v1.{Block, Blocks, Content, Crossword} import common.{DCRMetrics, GuLogging} import concurrent.CircuitBreakerRegistry import conf.Configuration @@ -10,6 +10,7 @@ import crosswords.CrosswordPageWithContent import http.{HttpPreconnections, ResultWithPreconnectPreload} import model.Cached.{RevalidatableResult, WithoutRevalidationResult} import model.dotcomrendering._ +import model.dotcomrendering.pageElements.EditionsCrosswordRenderingDataModel import model.{ CacheTime, Cached, @@ -417,6 +418,14 @@ class DotcomRenderingService extends GuLogging with ResultWithPreconnectPreload val json = DotcomRenderingDataModel.toJson(dataModel) post(ws, json, Configuration.rendering.articleBaseURL + "/Article", CacheTime.Facia) } + + def getEditionsCrossword( + ws: WSClient, + crosswords: EditionsCrosswordRenderingDataModel, + )(implicit request: RequestHeader): Future[Result] = { + val json = EditionsCrosswordRenderingDataModel.toJson(crosswords) + post(ws, json, Configuration.rendering.articleBaseURL + "/EditionsCrossword", CacheTime.Default) + } } object DotcomRenderingService { diff --git a/common/test/common/facia/PressedCollectionBuilder.scala b/common/test/common/facia/PressedCollectionBuilder.scala index 4a9dc049156..8b4aa0d5a3e 100644 --- a/common/test/common/facia/PressedCollectionBuilder.scala +++ b/common/test/common/facia/PressedCollectionBuilder.scala @@ -36,6 +36,7 @@ object PressedCollectionBuilder { showTimestamps = false, hideShowMore, displayHints = None, + aspectRatio = Some(""), ) PressedCollection( diff --git a/docs/01-start-here/05-development-tips.md b/docs/01-start-here/05-development-tips.md index 64413a92c3d..c1a92532fe4 100644 --- a/docs/01-start-here/05-development-tips.md +++ b/docs/01-start-here/05-development-tips.md @@ -26,7 +26,7 @@ make validate ``` These will fix the linting issues to make sure you won't have any issue trying to push or with the simple parts of the build. -If you have already committed you can use `make fix-commits` to verify & fix your commited code. It's +If you have already committed you can use `make fix-commits` to verify & fix your committed code. It's faster than `make fix` but you will need to amend your previous commits to get a clean history. If you are wondering what other options make has, you can simply type `make` at the comment line. @@ -68,4 +68,4 @@ without having to configure the debugger yourself: ### Viewing AMP Pages -[Use DCR](https://github.com/guardian/dotcom-rendering/blob/main/docs/architecture/amp/000-structure-for-initial-milestone.md) +[Use DCR](https://github.com/guardian/dotcom-rendering/blob/main/dotcom-rendering/docs/architecture/amp/000-structure-for-initial-milestone.md) diff --git a/package.json b/package.json index baf711b3f15..fd9b5b8df55 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "@emotion/react": "11.11.1", "@emotion/styled": "^10.0.27", "@guardian/ab-core": "8.0.0", - "@guardian/commercial": "23.7.0", + "@guardian/commercial": "23.7.4", "@guardian/core-web-vitals": "6.0.0", "@guardian/eslint-config-typescript": "9.0.1", "@guardian/identity-auth": "3.0.0", diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 8974c0df116..8283800dfaf 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -7,7 +7,7 @@ object Dependencies { val awsVersion = "1.12.758" val awsSdk2Version = "2.26.27" val capiVersion = "32.0.0" - val faciaVersion = "13.0.0" + val faciaVersion = "13.1.0" val dispatchVersion = "0.13.1" val romeVersion = "1.0" val jerseyVersion = "1.19.4" @@ -32,6 +32,7 @@ object Dependencies { val commonsIo = "commons-io" % "commons-io" % "2.16.1" val cssParser = "net.sourceforge.cssparser" % "cssparser" % "0.9.30" val contentApiClient = "com.gu" %% "content-api-client" % capiVersion + val contentApiModelsJson = "com.gu" %% "content-api-models-json" % "25.1.0" val dfpAxis = "com.google.api-ads" % "dfp-axis" % "5.6.0" val faciaFapiScalaClient = "com.gu" %% "fapi-client-play30" % faciaVersion val identityCookie = "com.gu.identity" %% "identity-cookie" % identityLibVersion diff --git a/static/src/javascripts/projects/common/modules/experiments/tests/onwards-content-article.js b/static/src/javascripts/projects/common/modules/experiments/tests/onwards-content-article.js new file mode 100644 index 00000000000..3cdf4fc812f --- /dev/null +++ b/static/src/javascripts/projects/common/modules/experiments/tests/onwards-content-article.js @@ -0,0 +1,24 @@ +export const onwardsContentArticle = { + id: 'onwardsContentArticle', + start: '2024-11-25', + expiry: '2025-01-29', + author: 'dotcom.platform@guardian.co.uk', + description: + 'Test the impact of showing the galleries onwards content component on article pages.', + audience: 0 / 100, + audienceOffset: 0 / 100, + audienceCriteria: 'Article pages', + successMeasure: + 'Users are more likely to click a link in the onward content component.', + canRun: () => true, + variants: [ + { + id: 'control', + test: () => {}, + }, + { + id: 'variant', + test: () => {}, + }, + ], +}; diff --git a/yarn.lock b/yarn.lock index 5c17a2afecf..1e9a106e39b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3607,9 +3607,9 @@ __metadata: languageName: node linkType: hard -"@guardian/commercial@npm:23.7.0": - version: 23.7.0 - resolution: "@guardian/commercial@npm:23.7.0" +"@guardian/commercial@npm:23.7.4": + version: 23.7.4 + resolution: "@guardian/commercial@npm:23.7.4" dependencies: "@guardian/prebid.js": "npm:8.52.0-8" "@octokit/core": "npm:^6.1.2" @@ -3626,7 +3626,7 @@ __metadata: "@guardian/libs": ^19.1.0 "@guardian/source": ^8.0.0 typescript: ~5.5.3 - checksum: 10c0/0cdff54bd95d094329fe9210a44c6717b36fd8fe364f4ebbd1a8d48eea929d883895b837e6257befa413d3da8ddec85519746bbd2bbcbce33d58d33d17203884 + checksum: 10c0/09fa6f6f237ff0a2bd81d4e9bf4203f4fe37bff33c963401217f5f5e513da505ccc9fab1284e8d732f8d77cff0927141fb7a67dfd60986f5fa91d27b47d5fd0a languageName: node linkType: hard @@ -3699,7 +3699,7 @@ __metadata: "@emotion/react": "npm:11.11.1" "@emotion/styled": "npm:^10.0.27" "@guardian/ab-core": "npm:8.0.0" - "@guardian/commercial": "npm:23.7.0" + "@guardian/commercial": "npm:23.7.4" "@guardian/core-web-vitals": "npm:6.0.0" "@guardian/eslint-config-typescript": "npm:9.0.1" "@guardian/identity-auth": "npm:3.0.0"