From 9310c013bca0f9418e749faf0db41319deef38e4 Mon Sep 17 00:00:00 2001 From: thanhz Date: Tue, 6 Aug 2024 11:22:18 +0100 Subject: [PATCH] TDRD-373 DTA Add email address to request details page (#4061) * New userDetails method * Replace userId with email in dta action page * Alter `MetadataReviewSubmittedEvent`to include status and userEmail * Read `READ_AUTH_SECRET` environment variable --- app/configuration/KeycloakConfiguration.scala | 17 ++++++++-- .../MetadataReviewActionController.scala | 10 ++++-- app/services/MessagingService.scala | 4 ++- app/views/tna/metadataReviewAction.scala.html | 8 ++--- build.sbt | 2 +- conf/application.base.conf | 1 + conf/routes | 2 +- .../MetadataReviewActionControllerSpec.scala | 32 +++++++++++-------- test/resources/application.conf | 1 + test/services/MessagingServiceSpec.scala | 8 +++-- test/testUtils/FrontEndTestHelper.scala | 2 ++ 11 files changed, 58 insertions(+), 29 deletions(-) diff --git a/app/configuration/KeycloakConfiguration.scala b/app/configuration/KeycloakConfiguration.scala index 96a1e1005..9a3a5e6bd 100644 --- a/app/configuration/KeycloakConfiguration.scala +++ b/app/configuration/KeycloakConfiguration.scala @@ -1,15 +1,26 @@ package configuration -import javax.inject.Inject import play.api.Configuration +import sttp.client3.{HttpURLConnectionBackend, Identity, SttpBackend} import uk.gov.nationalarchives.tdr.keycloak.{KeycloakUtils, TdrKeycloakDeployment, Token} -import scala.concurrent.ExecutionContext +import javax.inject.Inject +import scala.concurrent.{ExecutionContext, Future} class KeycloakConfiguration @Inject() (configuration: Configuration)(implicit val executionContext: ExecutionContext) { + implicit val backend: SttpBackend[Identity, Any] = HttpURLConnectionBackend() + val authUrl: String = configuration.get[String]("auth.url") + val secret: String = configuration.get[String]("read.auth.secret") + def token(value: String): Option[Token] = { implicit val tdrKeycloakDeployment: TdrKeycloakDeployment = - TdrKeycloakDeployment(s"${configuration.get[String]("auth.url")}", "tdr", 3600) + TdrKeycloakDeployment(authUrl, "tdr", 3600) KeycloakUtils().token(value).toOption } + + def userDetails(userId: String): Future[KeycloakUtils.UserDetails] = { + implicit val tdrKeycloakDeployment: TdrKeycloakDeployment = + TdrKeycloakDeployment(authUrl, "tdr", 3600) + KeycloakUtils().userDetails(userId, "tdr-user-read", secret) + } } diff --git a/app/controllers/MetadataReviewActionController.scala b/app/controllers/MetadataReviewActionController.scala index 28e7bcb0f..bc040bc0b 100644 --- a/app/controllers/MetadataReviewActionController.scala +++ b/app/controllers/MetadataReviewActionController.scala @@ -40,7 +40,7 @@ class MetadataReviewActionController @Inject() ( getConsignmentMetadataDetails(consignmentId, request, Ok, selectedDecisionForm) } - def submitReview(consignmentId: UUID, consignmentRef: String): Action[AnyContent] = tnaUserAction { implicit request: Request[AnyContent] => + def submitReview(consignmentId: UUID, consignmentRef: String, userEmail: String): Action[AnyContent] = tnaUserAction { implicit request: Request[AnyContent] => val formValidationResult: Form[SelectedStatusData] = selectedDecisionForm.bindFromRequest() val errorFunction: Form[SelectedStatusData] => Future[Result] = { formWithErrors: Form[SelectedStatusData] => @@ -57,8 +57,10 @@ class MetadataReviewActionController @Inject() ( } yield { messagingService.sendMetadataReviewSubmittedNotification( MetadataReviewSubmittedEvent( - consignmentReference = consignmentRef, - urlLink = generateUrlLink(request, routes.RequestMetadataReviewController.requestMetadataReviewPage(consignmentId).url) + consignmentRef, + urlLink = generateUrlLink(request, routes.MetadataReviewStatusController.metadataReviewStatusPage(consignmentId).url), + userEmail, + formData.statusId ) ) Redirect(routes.MetadataReviewController.metadataReviews()) @@ -76,11 +78,13 @@ class MetadataReviewActionController @Inject() ( ): Future[Result] = { for { consignment <- consignmentService.getConsignmentDetailForMetadataReview(consignmentId, request.token.bearerAccessToken) + userDetails <- keycloakConfiguration.userDetails(consignment.userid.toString) } yield { status( views.html.tna.metadataReviewAction( consignmentId, consignment, + userDetails.email, createDropDownField(List(InputNameAndValue("Approve", CompletedValue.value), InputNameAndValue("Reject", CompletedWithIssuesValue.value)), form) ) ) diff --git a/app/services/MessagingService.scala b/app/services/MessagingService.scala index d74756f2b..58cea20f0 100644 --- a/app/services/MessagingService.scala +++ b/app/services/MessagingService.scala @@ -52,6 +52,8 @@ object MessagingService { ) case class MetadataReviewSubmittedEvent( consignmentReference: String, - urlLink: String + urlLink: String, + userEmail: String, + status: String ) } diff --git a/app/views/tna/metadataReviewAction.scala.html b/app/views/tna/metadataReviewAction.scala.html index c01989070..8f81f4953 100644 --- a/app/views/tna/metadataReviewAction.scala.html +++ b/app/views/tna/metadataReviewAction.scala.html @@ -4,7 +4,7 @@ @import views.html.partials.{backLink, errorSummary, inputDropdown, downloadMetadataLink} @import java.util.UUID -@(consignmentId: UUID, consignmentDetails: gcdfmr.GetConsignment, dropdownField: DropdownField)(implicit request: RequestHeader, messages: Messages) +@(consignmentId: UUID, consignmentDetails: gcdfmr.GetConsignment, userEmail: String, dropdownField: DropdownField)(implicit request: RequestHeader, messages: Messages) @main("View Request for Metadata", hasError = dropdownField.fieldErrors.nonEmpty, backLink = Some(backLink(routes.MetadataReviewController.metadataReviews().url, "Back")), isTnaUser = true) {
@@ -39,10 +39,10 @@

- UserId + Contact email
- @consignmentDetails.userid + @userEmail
@@ -61,7 +61,7 @@

- @form(routes.MetadataReviewActionController.submitReview(consignmentId, consignmentDetails.consignmentReference), (Symbol("novalidate"), "")) { + @form(routes.MetadataReviewActionController.submitReview(consignmentId, consignmentDetails.consignmentReference, userEmail), (Symbol("novalidate"), "")) { @CSRF.formField @inputDropdown( dropdownField.fieldId, diff --git a/build.sbt b/build.sbt index 76f29318b..846344b54 100644 --- a/build.sbt +++ b/build.sbt @@ -28,7 +28,7 @@ libraryDependencies ++= Seq( "com.softwaremill.sttp.client" %% "circe" % sttpVersion, "com.softwaremill.sttp.client" %% "async-http-client-backend-future" % sttpVersion, "uk.gov.nationalarchives" %% "tdr-graphql-client" % "0.0.172", - "uk.gov.nationalarchives" %% "tdr-auth-utils" % "0.0.207", + "uk.gov.nationalarchives" %% "tdr-auth-utils" % "0.0.208", "uk.gov.nationalarchives" %% "tdr-generated-graphql" % "0.0.380", "uk.gov.nationalarchives" %% "tdr-metadata-validation" % "0.0.39", "uk.gov.nationalarchives" %% "s3-utils" % "0.1.196", diff --git a/conf/application.base.conf b/conf/application.base.conf index 9d1001e11..9d16b63b4 100644 --- a/conf/application.base.conf +++ b/conf/application.base.conf @@ -5,6 +5,7 @@ pekko.actor.allow-java-serialization = "on" pekko.actor.warn-about-java-serializer-usage = "off" auth.secret = ${AUTH_SECRET} +read.auth.secret = ${READ_AUTH_SECRET} auth.url=${AUTH_URL} consignmentapi.url = ${consignmentapi.domain}"/graphql" diff --git a/conf/routes b/conf/routes index cd2d557be..c1aca1bf7 100644 --- a/conf/routes +++ b/conf/routes @@ -82,4 +82,4 @@ GET /judgment/:consignmentId/transfer-complete # Routes for TNA-User GET /admin/metadata-review controllers.MetadataReviewController.metadataReviews() GET /admin/metadata-review/:consignmentId controllers.MetadataReviewActionController.consignmentMetadataDetails(consignmentId: java.util.UUID) -POST /admin/metadata-review/:consignmentId controllers.MetadataReviewActionController.submitReview(consignmentId: java.util.UUID, consignmentRef: String) +POST /admin/metadata-review/:consignmentId controllers.MetadataReviewActionController.submitReview(consignmentId: java.util.UUID, consignmentRef: String, userEmail: String) diff --git a/test/controllers/MetadataReviewActionControllerSpec.scala b/test/controllers/MetadataReviewActionControllerSpec.scala index aa09b2847..8be15ff1a 100644 --- a/test/controllers/MetadataReviewActionControllerSpec.scala +++ b/test/controllers/MetadataReviewActionControllerSpec.scala @@ -46,12 +46,11 @@ class MetadataReviewActionControllerSpec extends FrontEndTestHelper { "MetadataReviewActionController GET" should { - "render the correct series details page with an authenticated user" in { + "render the correct metadata details page with an authenticated user" in { setGetConsignmentDetailsForMetadataReviewResponse() val controller = instantiateMetadataReviewActionController(getAuthorisedSecurityComponents, getValidTNAUserKeycloakConfiguration) val metadataReviewActionPage = controller.consignmentMetadataDetails(consignmentId).apply(FakeRequest(GET, s"/admin/metadata-review/$consignmentId").withCSRFToken) - val metadataReviewActionPageAsString = contentAsString(metadataReviewActionPage) playStatus(metadataReviewActionPage) mustBe OK @@ -75,23 +74,27 @@ class MetadataReviewActionControllerSpec extends FrontEndTestHelper { status(response) mustBe FORBIDDEN } - "Update the consignment status and send metadata decision message when a valid form is submitted and the api response is successful" in { + "Update the consignment status and send MetadataReviewSubmittedEvent message when a valid form is submitted and the api response is successful" in { val controller = instantiateMetadataReviewActionController(getAuthorisedSecurityComponents, getValidTNAUserKeycloakConfiguration) val consignmentRef = "TDR-TEST-2024" - val expectedPath = s"/consignment/$consignmentId/metadata-review/request" + val userEmail = "test@test.com" + val status = CompletedValue.value + val expectedPath = s"/consignment/$consignmentId/metadata-review/review-progress" - // Custom ArgumentMatcher to match the event based on the consignment reference and the path - class MetadataReviewSubmittedEventMatcher(expectedConsignmentRef: String, expectedPath: String) extends ArgumentMatcher[MetadataReviewSubmittedEvent] { + // Custom ArgumentMatcher to match the event based on the consignment reference, path, userEmail and status + class MetadataReviewSubmittedEventMatcher(expectedConsignmentRef: String, expectedPath: String, expectedEmail: String, expectedStatus: String) + extends ArgumentMatcher[MetadataReviewSubmittedEvent] { override def matches(event: MetadataReviewSubmittedEvent): Boolean = { - event.consignmentReference == expectedConsignmentRef && event.urlLink.contains(expectedPath) + event.consignmentReference == expectedConsignmentRef && event.urlLink.contains(expectedPath) && event.userEmail == expectedEmail && event.status == expectedStatus } } - val metadataReviewDecisionEventMatcher = new MetadataReviewSubmittedEventMatcher(consignmentRef, expectedPath) + val metadataReviewDecisionEventMatcher = new MetadataReviewSubmittedEventMatcher(consignmentRef, expectedPath, userEmail, status) setUpdateConsignmentStatus(wiremockServer) - val reviewSubmit = controller.submitReview(consignmentId, consignmentRef).apply(FakeRequest().withFormUrlEncodedBody(("status", CompletedValue.value)).withCSRFToken) + val reviewSubmit = + controller.submitReview(consignmentId, consignmentRef, userEmail).apply(FakeRequest().withFormUrlEncodedBody(("status", status)).withCSRFToken) playStatus(reviewSubmit) mustBe SEE_OTHER redirectLocation(reviewSubmit) must be(Some(s"/admin/metadata-review")) verify(messagingService, times(1)).sendMetadataReviewSubmittedNotification(argThat(metadataReviewDecisionEventMatcher)) @@ -100,8 +103,9 @@ class MetadataReviewActionControllerSpec extends FrontEndTestHelper { "display errors when an invalid form is submitted" in { val controller = instantiateMetadataReviewActionController(getAuthorisedSecurityComponents, getValidTNAUserKeycloakConfiguration) val consignmentRef = "TDR-TEST-2024" - val metadataReviewDecisionEvent = MetadataReviewSubmittedEvent(consignmentRef, "SomeUrl") - val reviewSubmit = controller.submitReview(consignmentId, consignmentRef).apply(FakeRequest().withFormUrlEncodedBody(("status", "")).withCSRFToken) + val userEmail = "test@test.com" + val metadataReviewDecisionEvent = MetadataReviewSubmittedEvent(consignmentRef, "SomeUrl", userEmail, "status") + val reviewSubmit = controller.submitReview(consignmentId, consignmentRef, userEmail).apply(FakeRequest().withFormUrlEncodedBody(("status", "")).withCSRFToken) setUpdateConsignmentStatus(wiremockServer) setGetConsignmentDetailsForMetadataReviewResponse() playStatus(reviewSubmit) mustBe BAD_REQUEST @@ -174,15 +178,15 @@ class MetadataReviewActionControllerSpec extends FrontEndTestHelper { | SeriesName | """.stripMargin) pageAsString must include(s"""
- | UserId + | Contact email |
|
- | $userId + | email@test.com |
""".stripMargin) pageAsString must include("""1. Download and review transfer metadata""") pageAsString must include(downloadLinkHTML(consignmentId)) pageAsString must include("""2. Set the status of this review""") - pageAsString must include(s"""
""") + pageAsString must include(s"""""") pageAsString must include(s"""""".stripMargin) diff --git a/test/resources/application.conf b/test/resources/application.conf index b2ebd73ea..07819f68b 100644 --- a/test/resources/application.conf +++ b/test/resources/application.conf @@ -5,6 +5,7 @@ auth.domain="localhost:9005" auth.url="http://localhost:9005" auth.callback="http://localhost:9000/callback" auth.secret="f5d12779-2927-4992-a7cf-77358cf42e20" +read.auth.secret="f5d12779-2927-4992-a7cf-77358cf42e20" consignmentapi.url="http://localhost:9006/graphql" diff --git a/test/services/MessagingServiceSpec.scala b/test/services/MessagingServiceSpec.scala index b5760a5e9..2bb2c88eb 100644 --- a/test/services/MessagingServiceSpec.scala +++ b/test/services/MessagingServiceSpec.scala @@ -65,11 +65,15 @@ class MessagingServiceSpec extends AnyFlatSpec with Matchers { val service = createService val metadataReviewSubmittedEvent = MessagingService.MetadataReviewSubmittedEvent( consignmentReference = "Ref123", - urlLink = "example.com" + urlLink = "example.com", + userEmail = "user@example.com", + status = "Status" ) val expectedMessageString = """{ | "consignmentReference" : "Ref123", - | "urlLink" : "example.com" + | "urlLink" : "example.com", + | "userEmail" : "user@example.com", + | "status" : "Status" |}""".stripMargin service.sendMetadataReviewSubmittedNotification(metadataReviewSubmittedEvent) verify(mockUtils).publish(expectedMessageString, testArn) diff --git a/test/testUtils/FrontEndTestHelper.scala b/test/testUtils/FrontEndTestHelper.scala index bbefe428f..86f95bf0b 100644 --- a/test/testUtils/FrontEndTestHelper.scala +++ b/test/testUtils/FrontEndTestHelper.scala @@ -75,6 +75,7 @@ import play.api.test.Injecting import play.api.{Application, Configuration} import services.Statuses.{InProgressValue, SeriesType} import uk.gov.nationalarchives.tdr.GraphQLClient +import uk.gov.nationalarchives.tdr.keycloak.KeycloakUtils.UserDetails import uk.gov.nationalarchives.tdr.keycloak.Token import viewsapi.FrontEndInfo @@ -929,6 +930,7 @@ trait FrontEndTestHelper extends PlaySpec with MockitoSugar with Injecting with accessToken.setEmail("test@example.com") val token = Token(accessToken, new BearerAccessToken) doAnswer(_ => Some(token)).when(keycloakMock).token(any[String]) + when(keycloakMock.userDetails(any[String])).thenReturn(Future(UserDetails("email@test.com"))) keycloakMock }