Skip to content

Commit

Permalink
Merge pull request #3906 from nationalarchives/TDRD-222-transfer-subm…
Browse files Browse the repository at this point in the history
…itted-sns

[TDRD 222] Send transfer submitted message to notifications sns topic
  • Loading branch information
annielh authored Jun 7, 2024
2 parents a87c849 + f232e7b commit 441a8e5
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 11 deletions.
4 changes: 4 additions & 0 deletions app/configuration/ApplicationConfig.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ class ApplicationConfig @Inject() (configuration: Configuration) {

val s3Endpoint: String = configuration.get[String]("s3.endpoint")

val snsEndpoint: String = configuration.get[String]("sns.endpoint")

val draft_metadata_s3_bucket_name: String = configuration.get[String]("draft_metadata_s3_bucket_name")

val draftMetadataFileName: String = configuration.get[String]("draftMetadata.fileName")

val notificationSnsTopicArn: String = get("notificationSnsTopicArn")
}
26 changes: 19 additions & 7 deletions app/controllers/TransferCompleteController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import configuration.KeycloakConfiguration
import org.pac4j.play.scala.SecurityComponents
import play.api.i18n.I18nSupport
import play.api.mvc.{Action, AnyContent, Request}
import services.ConsignmentService
import services.MessagingService.TransferCompleteEvent
import services.{ConsignmentService, MessagingService}

import java.util.UUID
import javax.inject.Inject
Expand All @@ -14,17 +15,28 @@ import scala.concurrent.ExecutionContext
class TransferCompleteController @Inject() (
val controllerComponents: SecurityComponents,
val keycloakConfiguration: KeycloakConfiguration,
val consignmentService: ConsignmentService
val consignmentService: ConsignmentService,
val messagingService: MessagingService
)(implicit val ec: ExecutionContext)
extends TokenSecurity
with I18nSupport {

def transferComplete(consignmentId: UUID): Action[AnyContent] = standardTypeAction(consignmentId) { implicit request: Request[AnyContent] =>
consignmentService
.getConsignmentRef(consignmentId, request.token.bearerAccessToken)
.map { consignmentReference =>
Ok(views.html.standard.transferComplete(consignmentId, consignmentReference, request.token.name))
}
for {
consignmentTransferSummary <- consignmentService.getConsignmentConfirmTransfer(consignmentId, request.token.bearerAccessToken)
} yield {
messagingService.sendTransferCompleteNotification(
TransferCompleteEvent(
transferringBodyName = consignmentTransferSummary.transferringBodyName,
consignmentReference = consignmentTransferSummary.consignmentReference,
consignmentId = consignmentId.toString,
seriesName = consignmentTransferSummary.seriesName,
userId = request.token.userId.toString,
userEmail = request.token.email
)
)
Ok(views.html.standard.transferComplete(consignmentId, consignmentTransferSummary.consignmentReference, request.token.name))
}
}

def judgmentTransferComplete(consignmentId: UUID): Action[AnyContent] = judgmentTypeAction(consignmentId) { implicit request: Request[AnyContent] =>
Expand Down
35 changes: 35 additions & 0 deletions app/services/MessagingService.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package services

import configuration.ApplicationConfig
import io.circe.Encoder
import io.circe.generic.semiauto.deriveEncoder
import io.circe.syntax.EncoderOps
import services.MessagingService.TransferCompleteEvent
import software.amazon.awssdk.services.sns.SnsClient
import software.amazon.awssdk.services.sns.model.PublishResponse
import uk.gov.nationalarchives.aws.utils.sns.SNSClients.sns
import uk.gov.nationalarchives.aws.utils.sns.SNSUtils

import javax.inject.Inject
import scala.concurrent.ExecutionContext

class MessagingService @Inject() (val applicationConfig: ApplicationConfig)(implicit val ec: ExecutionContext) {
val client: SnsClient = sns(applicationConfig.snsEndpoint)
val utils: SNSUtils = SNSUtils(client)

implicit val transferCompletedEventEncoder: Encoder[TransferCompleteEvent] = deriveEncoder[TransferCompleteEvent]
def sendTransferCompleteNotification(transferCompletedEvent: TransferCompleteEvent): PublishResponse = {
utils.publish(transferCompletedEvent.asJson.toString, applicationConfig.notificationSnsTopicArn)
}
}

object MessagingService {
case class TransferCompleteEvent(
transferringBodyName: Option[String],
consignmentReference: String,
consignmentId: String,
seriesName: Option[String],
userId: String,
userEmail: String
)
}
1 change: 1 addition & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ libraryDependencies ++= Seq(
"uk.gov.nationalarchives" %% "tdr-generated-graphql" % "0.0.376",
"uk.gov.nationalarchives" %% "tdr-metadata-validation" % "0.0.23",
"uk.gov.nationalarchives" %% "s3-utils" % "0.1.172",
"uk.gov.nationalarchives" %% "sns-utils" % "0.1.173",
"com.github.tototoshi" %% "scala-csv" % "1.3.10",
"ch.qos.logback" % "logback-classic" % "1.5.6",
ws,
Expand Down
4 changes: 4 additions & 0 deletions conf/application.base.conf
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ logout.url="http://localhost:9000/signed-out"

s3.endpoint = "https://s3.eu-west-2.amazonaws.com/"

sns.endpoint = "https://sns.eu-west-2.amazonaws.com/"

play.filters.enabled += play.filters.https.RedirectHttpsFilter

play {
Expand Down Expand Up @@ -62,3 +64,5 @@ featureAccessBlock {
draftMetadata {
fileName = "draft-metadata.csv"
}

notificationSnsTopicArn = ${NOTIFICATION_SNS_TOPIC_ARN}
10 changes: 7 additions & 3 deletions test/controllers/TransferCompleteControllerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import play.api.mvc.Result
import play.api.test.CSRFTokenHelper.CSRFRequest
import play.api.test.FakeRequest
import play.api.test.Helpers.{GET, contentAsString, defaultAwaitTimeout, status}
import services.ConsignmentService
import services.{ConsignmentService, MessagingService}
import testUtils.{CheckPageForStaticElements, FrontEndTestHelper}

import java.time.{LocalDateTime, ZonedDateTime}
Expand Down Expand Up @@ -45,6 +45,7 @@ class TransferCompleteControllerSpec extends FrontEndTestHelper {
"TransferCompleteController GET" should {
"render the success page if the export was triggered successfully" in {
setConsignmentReferenceResponse(wiremockServer)
setConsignmentSummaryResponse(wiremockServer)
val consignmentId = UUID.randomUUID()
val transferCompletePage = callTransferComplete("consignment", consignmentId)
val transferCompletePageAsString = contentAsString(transferCompletePage)
Expand Down Expand Up @@ -86,6 +87,7 @@ class TransferCompleteControllerSpec extends FrontEndTestHelper {

"render the success page if the export was triggered successfully for a judgment user" in {
setConsignmentReferenceResponse(wiremockServer)
setConsignmentSummaryResponse(wiremockServer)
val transferCompletePage = callTransferComplete("judgment")
val transferCompletePageAsString = contentAsString(transferCompletePage)

Expand Down Expand Up @@ -125,6 +127,7 @@ class TransferCompleteControllerSpec extends FrontEndTestHelper {
s"The $url upload page" should {
s"return 403 if the url doesn't match the consignment type" in {
setConsignmentReferenceResponse(wiremockServer)
setConsignmentSummaryResponse(wiremockServer)
val controller = instantiateTransferCompleteController(getAuthorisedSecurityComponents, url)
val consignmentId = UUID.randomUUID()
setConsignmentTypeResponse(wiremockServer, url)
Expand All @@ -148,10 +151,11 @@ class TransferCompleteControllerSpec extends FrontEndTestHelper {
private def instantiateTransferCompleteController(securityComponents: SecurityComponents, path: String) = {
val graphQLConfiguration = new GraphQLConfiguration(app.configuration)
val consignmentService = new ConsignmentService(graphQLConfiguration)
val messagingService = mock[MessagingService]
if (path.equals("judgment")) {
new TransferCompleteController(securityComponents, getValidJudgmentUserKeycloakConfiguration, consignmentService)
new TransferCompleteController(securityComponents, getValidJudgmentUserKeycloakConfiguration, consignmentService, messagingService)
} else {
new TransferCompleteController(securityComponents, getValidStandardUserKeycloakConfiguration, consignmentService)
new TransferCompleteController(securityComponents, getValidStandardUserKeycloakConfiguration, consignmentService, messagingService)
}
}

Expand Down
1 change: 1 addition & 0 deletions test/resources/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ export.baseUrl = "http://localhost:9007"
backendchecks.baseUrl = "http://localhost:9008"
s3.endpoint = "https://s3.eu-west-2.amazonaws.com/"
draft_metadata_s3_bucket_name = "tdr-draft_metadata"
notificationSnsTopicArn = "arn:test-arn"
49 changes: 49 additions & 0 deletions test/services/MessagingServiceSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package services

import configuration.ApplicationConfig
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import uk.gov.nationalarchives.aws.utils.sns.SNSUtils
import org.mockito.Mockito.{verify, when}
import org.scalatestplus.mockito.MockitoSugar.mock
import play.api.Configuration
import software.amazon.awssdk.services.sns.SnsClient

import scala.concurrent.ExecutionContext

class MessagingServiceSpec extends AnyFlatSpec with Matchers {

implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global
val mockSnsClient: SnsClient = mock[SnsClient]
val mockUtils: SNSUtils = mock[SNSUtils]
val config: Configuration = mock[Configuration]

"sendTransferCompleteNotification" should "call publish with the correct parameters" in {
val testArn = "arn:test-arn"
when(config.get[String]("sns.endpoint")).thenReturn("http://localhost:9009")
when(config.get[String]("notificationSnsTopicArn")).thenReturn(testArn)
val appConfig = new ApplicationConfig(config)
val service = new MessagingService(appConfig)(ec) {
override val client: SnsClient = mockSnsClient
override val utils: SNSUtils = mockUtils
}
val transferCompleteEvent = MessagingService.TransferCompleteEvent(
transferringBodyName = Some("TransferringBodyName"),
consignmentReference = "Ref123",
consignmentId = "ConsID456",
seriesName = Some("SeriesXYZ"),
userId = "UserID789",
userEmail = "user@example.com"
)
val expectedMessageString = """{
| "transferringBodyName" : "TransferringBodyName",
| "consignmentReference" : "Ref123",
| "consignmentId" : "ConsID456",
| "seriesName" : "SeriesXYZ",
| "userId" : "UserID789",
| "userEmail" : "user@example.com"
|}""".stripMargin
service.sendTransferCompleteNotification(transferCompleteEvent)
verify(mockUtils).publish(expectedMessageString, testArn)
}
}
30 changes: 29 additions & 1 deletion test/testUtils/FrontEndTestHelper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import graphql.codegen.GetConsignmentFilesMetadata.{getConsignmentFilesMetadata
import graphql.codegen.GetConsignmentStatus.getConsignmentStatus.GetConsignment
import graphql.codegen.GetConsignmentStatus.getConsignmentStatus.GetConsignment.ConsignmentStatuses
import graphql.codegen.GetConsignmentStatus.{getConsignmentStatus => gcs}
import graphql.codegen.GetConsignmentSummary.{getConsignmentSummary => gcsu}
import graphql.codegen.GetConsignments.getConsignments.Consignments
import graphql.codegen.GetConsignments.getConsignments.Consignments.Edges
import graphql.codegen.GetConsignments.getConsignments.Consignments.Edges.Node
Expand Down Expand Up @@ -70,7 +71,6 @@ import play.api.{Application, Configuration}
import services.Statuses.{InProgressValue, SeriesType}
import uk.gov.nationalarchives.tdr.GraphQLClient
import uk.gov.nationalarchives.tdr.keycloak.Token
import graphql.codegen.GetConsignmentStatus.getConsignmentStatus.GetConsignment
import org.pac4j.core.context.{CallContext, FrameworkParameters}
import org.pac4j.oidc.metadata.OidcOpMetadataResolver
import services.Statuses.{InProgressValue, SeriesType}
Expand Down Expand Up @@ -261,6 +261,34 @@ trait FrontEndTestHelper extends PlaySpec with MockitoSugar with Injecting with
)
}

def setConsignmentSummaryResponse(
wiremockServer: WireMockServer,
seriesName: Option[String] = None,
transferringBodyName: Option[String] = None,
totalFiles: Int = 0,
consignmentReference: String = "TEST-TDR-2021-GB"
): StubMapping = {
val client = new GraphQLConfiguration(app.configuration).getClient[gcsu.Data, gcsu.Variables]()
val consignmentSummaryResponse = gcsu.Data(
Option(
gcsu.GetConsignment(
seriesName,
transferringBodyName,
totalFiles,
consignmentReference
)
)
)
val data: client.GraphqlData = client.GraphqlData(Some(consignmentSummaryResponse))
val dataString: String = data.asJson.printWith(Printer(dropNullValues = false, ""))

wiremockServer.stubFor(
post(urlEqualTo("/graphql"))
.withRequestBody(containing("getConsignmentSummary"))
.willReturn(okJson(dataString))
)
}

def setUpdateConsignmentStatus(wiremockServer: WireMockServer): StubMapping = {
val client = new GraphQLConfiguration(app.configuration).getClient[ucs.Data, ucs.Variables]()
val data = client.GraphqlData(Option(ucs.Data(Option(1))), Nil)
Expand Down

0 comments on commit 441a8e5

Please sign in to comment.