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

Use JsValue Not String For DCAR #27641

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

JamieB-gu
Copy link
Contributor

@JamieB-gu JamieB-gu commented Nov 27, 2024

Frontend posts the data for articles, fronts, tags pages and so on to DCAR as JSON, using WSClient. At the moment the following steps are taken to transform from an instance of a case class in frontend to the format that WSClient uses (BodyWritable):

case class instance -> JsValue -> String -> BodyWritable

The final transformation here, String -> BodyWritable, is handled by WSClient, as it defines an implicit way to get from one type to the other1. However, it also defines a way to do this for a play-json JsValue2, so we do not need to convert to a String beforehand. We should be able to pass a JsValue to WSClient directly, removing one of the transformation steps:

case class instance -> JsValue -> BodyWritable

The way to achieve this is by modifying our methods that use WSClient.post to take a JsValue payload instead of a String. We are then able to update the toJson methods on each of our models to remove the Json.stringify step. However, aside from RemoteRender requests, these toJson methods are also used to provide for JsonFormat requests via the renderJson method, which at the moment requires a String. This method, though, makes use of Play's Writeable, which also includes an implicit transformation to get from a play-json JsValue3. So we can update it to take a JsValue instead, and save the additional String transformation step here too.

Note that there is one exception where we're using circe instead of play-json, for crosswords, so in that case we're transforming from one to the other via String.

Paired with @rtyley

Footnotes

  1. https://www.playframework.com/documentation/3.0.x/api/scala/play/api/libs/ws/WSBodyWritables$.html#writeableOf_String:play.api.libs.ws.BodyWritable[String]

  2. https://www.playframework.com/documentation/3.0.x/api/scala/play/api/libs/ws/WSBodyWritables$.html#writeableOf_JsValue:play.api.libs.ws.BodyWritable[play.api.libs.json.JsValue]

  3. https://www.playframework.com/documentation/3.0.x/api/scala/play/api/http/Writeable$.html#writeableOf_JsValue:play.api.http.Writeable[play.api.libs.json.JsValue]

Frontend posts the data for articles, fronts, tags pages and so on to DCAR as JSON, using `WSClient`. At the moment the following steps are taken to transform from an instance of a case class in frontend to the format that `WSClient` uses (`BodyWritable`):

```
case class instance -> JsValue -> String -> BodyWritable
```

The final transformation here, `String -> BodyWritable`, is handled by `WSClient`, as it defines an implicit way to get from one type to the other. However, it also defines a way to do this for a `play-json` `JsValue`, so we do not need to convert to a `String` beforehand. We should be able to pass a `JsValue` to `WSClient` directly, removing one of the transformation steps:

```
case class instance -> JsValue -> BodyWritable
```

The way to achieve this is by modifying our methods that use `WSClient.post` to take a `JsValue` payload instead of a `String`. We are then able to update the `toJson` methods on each of our models to remove the `Json.stringify` step. However, aside from `RemoteRender` requests, these `toJson` methods are also used to provide for `JsonFormat` requests via the `renderJson` method, which at the moment requires a `String`. This method, though, makes use of Play's `Writeable`, which also includes an implicit transformation to get from a `play-json` `JsValue`. So we can update it to take a `JsValue` instead, and save the additional `String` transformation step here too.

Note that there is one exception where we're using `circe` instead of `play-json`, for crosswords, so in that case we're transforming from one to the other via `String`.
@JamieB-gu JamieB-gu added this to the Health milestone Nov 27, 2024
@JamieB-gu JamieB-gu self-assigned this Nov 27, 2024
@rtyley
Copy link
Member

rtyley commented Nov 27, 2024

Lovely stuff! I see the tests have failed, but from a quick look at the logs, maybe the only failing test was DotcomRenderingServiceTest:

[info] DotcomRenderingServiceTest:
[info] post
[info] - should return a 404 for DCR 415 errors *** FAILED ***
[info]   java.lang.IllegalArgumentException: Can't find a private method named: post
[info]   at org.scalatest.PrivateMethodTester$Invoker.invokePrivate(PrivateMethodTester.scala:247)
[info]   at renderers.DotcomRenderingServiceTest.$anonfun$new$1(DotcomRenderingServiceTest.scala:52)

...this is because the test is mocking the post() method, which has been changed to take a JsValue payload, rather that a String:

val payload = "payload"
when(wsMock.url(any[String])).thenReturn(wsRequestMock)
when(wsRequestMock.withRequestTimeout(any())).thenReturn(wsRequestMock)
when(wsRequestMock.addHttpHeaders(any())).thenReturn(wsRequestMock)
when(wsRequestMock.post(payload)).thenReturn(Future.successful(wsResponseMock))
when(wsResponseMock.status).thenReturn(415)
whenReady(
dotcomRenderingService invokePrivate post(
wsMock,
payload,
"https://endpoint.com",
CacheTime.Default,
Configuration.rendering.timeout,
request,
),
) { result =>
result.header.status should be(404)

Incidentally, it was quite hard to search the logs for the failure. Using something like https://github.com/test-summary/action or https://github.com/EnricoMi/publish-unit-test-result-action could make finding failing tests on Frontend much friendlier?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Review
Development

Successfully merging this pull request may close these issues.

2 participants