diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7310e1259..e0c6383c2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,6 +3,7 @@ on: push: branches: - master + - test-load-testing-for-judgment jobs: pre-deploy: uses: nationalarchives/tdr-github-actions/.github/workflows/ecs_build.yml@main diff --git a/app/controllers/FileChecksController.scala b/app/controllers/FileChecksController.scala index d3fbb1d62..8f4de6638 100644 --- a/app/controllers/FileChecksController.scala +++ b/app/controllers/FileChecksController.scala @@ -7,14 +7,14 @@ import graphql.codegen.types.ConsignmentStatusInput import io.circe.syntax._ import org.pac4j.play.scala.SecurityComponents import play.api.i18n.I18nSupport -import play.api.mvc.{Action, AnyContent, Request, RequestHeader} -import services.Statuses.{CompletedValue, CompletedWithIssuesValue, UploadType} -import services.{BackendChecksService, ConsignmentService, ConsignmentStatusService} +import play.api.mvc._ +import services.Statuses._ +import services._ import viewsapi.Caching.preventCaching import java.util.UUID import javax.inject.{Inject, Singleton} -import scala.concurrent.{ExecutionContext, Future} +import scala.concurrent.{ExecutionContext, Future, blocking} @Singleton class FileChecksController @Inject() ( @@ -24,7 +24,9 @@ class FileChecksController @Inject() ( val consignmentService: ConsignmentService, val applicationConfig: ApplicationConfig, val backendChecksService: BackendChecksService, - val consignmentStatusService: ConsignmentStatusService + val consignmentStatusService: ConsignmentStatusService, + val confirmTransferService: ConfirmTransferService, + val consignmentExportService: ConsignmentExportService )(implicit val ec: ExecutionContext) extends TokenSecurity with I18nSupport { @@ -71,6 +73,13 @@ class FileChecksController @Inject() ( .map(Ok(_)) } + def exportProgress(consignmentId: UUID): Action[AnyContent] = secureAction.async { implicit request => { + consignmentStatusService.getConsignmentStatuses(consignmentId, request.token.bearerAccessToken) + .map(consignmentStatuses => consignmentStatusService.getStatusValues(consignmentStatuses, ExportType).values.headOption.flatten) + .map(status => Ok(status.getOrElse(""))) + } + } + def fileChecksPage(consignmentId: UUID, uploadFailed: Option[String]): Action[AnyContent] = standardTypeAction(consignmentId) { implicit request: Request[AnyContent] => val token = request.token.bearerAccessToken for { @@ -93,6 +102,24 @@ class FileChecksController @Inject() ( } yield result } + def judgmentCompleteTransfer(consignmentId: UUID): Action[AnyContent] = judgmentTypeAction(consignmentId) { implicit request: Request[AnyContent] => + val token = request.token.bearerAccessToken + for { + reference <- consignmentService.getConsignmentRef(consignmentId, token) + result <- + ( + for { + _ <- waitForFileChecksToBeCompleted(consignmentId) + result <- JudgmentCompleteTransfer(consignmentId) + } yield result + ).recover { case _: Exception => + Ok(views.html.uploadInProgress(consignmentId, reference, "Uploading your records", request.token.name, isJudgmentUser = true)).uncache() + } + } yield { + result + } + } + private def handleSuccessfulUpload(consignmentId: UUID, reference: String, isJudgmentUser: Boolean)(implicit request: Request[AnyContent]) = { val token = request.token.bearerAccessToken (for { @@ -129,6 +156,62 @@ class FileChecksController @Inject() ( Ok(views.html.uploadInProgress(consignmentId, reference, "Uploading your records", request.token.name, isJudgmentUser)).uncache() } } + + private def JudgmentCompleteTransfer(consignmentId: UUID)(implicit request: Request[AnyContent]): Future[Result] = { + val pageTitle = "Results of checks" + for { + consignmentStatuses <- consignmentStatusService.getConsignmentStatuses(consignmentId, request.token.bearerAccessToken) + reference <- consignmentService.getConsignmentRef(consignmentId, request.token.bearerAccessToken) + exportStatus = consignmentStatusService.getStatusValues(consignmentStatuses, ExportType).values.headOption.flatten + result <- exportStatus match { + case Some(InProgressValue.value) | Some(CompletedValue.value) | Some(FailedValue.value) => + Future(Ok(views.html.transferAlreadyCompleted(consignmentId, reference, request.token.name, isJudgmentUser = true)).uncache()) + case None => + for { + fileCheck <- consignmentService.fileCheckProgress(consignmentId, request.token.bearerAccessToken) + result <- + if (fileCheck.allChecksSucceeded) { + val token: BearerAccessToken = request.token.bearerAccessToken + val legalCustodyTransferConfirmation = FinalTransferConfirmationData(transferLegalCustody = true) + for { + _ <- confirmTransferService.addFinalTransferConfirmation(consignmentId, token, legalCustodyTransferConfirmation) + _ <- consignmentExportService.updateTransferInitiated(consignmentId, request.token.bearerAccessToken) + _ <- consignmentExportService.triggerExport(consignmentId, request.token.bearerAccessToken.toString) + res <- Future(Ok("{}").uncache()) + } yield res + } else { + Future(Ok(views.html.fileChecksResultsFailed(request.token.name, pageTitle, reference, isJudgmentUser = true)).uncache()) + } + } yield result + case _ => + throw new IllegalStateException(s"Unexpected Export status: $exportStatus for consignment $consignmentId") + } + } yield result + } + + private def waitForFileChecksToBeCompleted(consignmentId: UUID)(implicit request: Request[AnyContent]): Future[Unit] = { + val totalSleepTime = 480 * 1000 // Total sleep time in milliseconds + val interval = 5 * 1000 // Interval time in milliseconds (5 seconds) + val intervals = totalSleepTime / interval + + def checkStatus(remainingIntervals: Int): Future[Unit] = { + if (remainingIntervals <= 0) { + Future.unit + } else { + getFileChecksProgress(request, consignmentId).flatMap { progress => + if (progress.isComplete) { + Future.unit + } else { + Future { + blocking(Thread.sleep(interval)) + }.flatMap(_ => checkStatus(remainingIntervals - 1)) + } + } + } + } + + checkStatus(intervals) + } } case class FileChecksProgress(totalFiles: Int, avMetadataProgressPercentage: Int, checksumProgressPercentage: Int, ffidMetadataProgressPercentage: Int) { diff --git a/app/views/judgment/judgmentFileChecksProgress.scala.html b/app/views/judgment/judgmentFileChecksProgress.scala.html index 14313bcfe..9c7cb6131 100644 --- a/app/views/judgment/judgmentFileChecksProgress.scala.html +++ b/app/views/judgment/judgmentFileChecksProgress.scala.html @@ -20,7 +20,7 @@

Checking your upload

- @form(routes.FileChecksResultsController.judgmentFileCheckResultsPage(consignmentId), Symbol("id") -> "file-checks-form") { } + @form(routes.TransferCompleteController.judgmentTransferComplete(consignmentId), Symbol("id") -> "file-checks-form") { } @transferReference(consignmentRef, isJudgmentUser = true) diff --git a/conf/routes b/conf/routes index cd2d557be..46f972620 100644 --- a/conf/routes +++ b/conf/routes @@ -76,6 +76,8 @@ GET /judgment/:consignmentId/before-uploading GET /judgment/:consignmentId/upload controllers.UploadController.judgmentUploadPage(consignmentId: java.util.UUID) GET /judgment/:consignmentId/file-checks controllers.FileChecksController.judgmentFileChecksPage(consignmentId: java.util.UUID, uploadFailed: Option[String]) POST /judgment/:consignmentId/file-check-progress controllers.FileChecksController.fileCheckProgress(consignmentId: java.util.UUID) +POST /judgment/:consignmentId/export-progress controllers.FileChecksController.exportProgress(consignmentId: java.util.UUID) +POST /judgment/:consignmentId/continue-transfer controllers.FileChecksController.judgmentCompleteTransfer(consignmentId: java.util.UUID) GET /judgment/:consignmentId/file-checks-results controllers.FileChecksResultsController.judgmentFileCheckResultsPage(consignmentId: java.util.UUID) GET /judgment/:consignmentId/transfer-complete controllers.TransferCompleteController.judgmentTransferComplete(consignmentId: java.util.UUID) diff --git a/npm/src/checks/get-checks-progress.ts b/npm/src/checks/get-checks-progress.ts index d3fb8dbac..f3baa9ba7 100644 --- a/npm/src/checks/get-checks-progress.ts +++ b/npm/src/checks/get-checks-progress.ts @@ -83,9 +83,35 @@ export const getFileChecksProgress: () => Promise< ) } +export const getExportProgress: () => Promise< + Boolean | Error +> = async () => { + const progress = await getProgress("export-progress") + if (!isError(progress)) { + console.log("progress >>>>>>>" + progress) + const response = progress as String + console.log("Resposnse >>>>>>>" + response) + return response === "InProgress" || response === "Failed" || response === "Completed" + } else + return Error( + `Failed to retrieve progress for file checks: ${progress.message}` + ) +} + +export const continueTransfer: () => Promise< + String | Error +> = async () => { + const progress = await getProgress("continue-transfer") + if (isError(progress)) { + return Error( + `Failed to retrieve progress for file checks: ${progress.message}` + ) + } +} + const getProgress: ( progressEndpoint: string -) => Promise = async (progressEndpoint) => { +) => Promise = async (progressEndpoint) => { const consignmentId = getConsignmentId() if (!isError(consignmentId)) { const csrfInput: HTMLInputElement = document.querySelector( @@ -109,7 +135,11 @@ const getProgress: ( } else if (result.status != 200) { return Error(`Retrieving progress failed: ${result.statusText}`) } else { - return await result.json() + if (progressEndpoint === "export-progress") { + return await result.text() + } else { + return await result.json() + } } } else { return Error(`Failed to retrieve consignment id: ${consignmentId.message}`) diff --git a/npm/src/checks/index.ts b/npm/src/checks/index.ts index e06d3b0e0..0423a019b 100644 --- a/npm/src/checks/index.ts +++ b/npm/src/checks/index.ts @@ -4,7 +4,8 @@ import { } from "./verify-checks-have-completed" import { displayChecksCompletedBanner } from "./display-checks-completed-banner" import { - getDraftMetadataValidationProgress, + continueTransfer, + getDraftMetadataValidationProgress, getExportProgress, getFileChecksProgress, IDraftMetadataValidationProgress, IFileCheckProgress @@ -12,27 +13,22 @@ import { import { isError } from "../errorhandling" export class Checks { - updateFileCheckProgress: ( - isJudgmentUser: boolean, - goToNextPage: (formId: string) => void - ) => void | Error = ( - isJudgmentUser: boolean, - goToNextPage: (formId: string) => void + updateFileCheckProgress: (isJudgmentUser: boolean, goToNextPage: (formId: string) => void) => Promise = async ( + isJudgmentUser: boolean, + goToNextPage: (formId: string) => void ) => { + continueTransfer() const intervalId: ReturnType = setInterval(async () => { - const fileChecksProgress: IFileCheckProgress | Error = - await getFileChecksProgress() - if (!isError(fileChecksProgress)) { - const checksCompleted = haveFileChecksCompleted(fileChecksProgress) - if (checksCompleted) { - clearInterval(intervalId) - isJudgmentUser - ? goToNextPage("#file-checks-form") - : displayChecksCompletedBanner("file-checks") + const isCompleted: Boolean | Error = await getExportProgress() + if (!isError(isCompleted)) { + const v = isCompleted as boolean + if (v) { + clearInterval(intervalId) + goToNextPage("#file-checks-form") + } + } else { + return isCompleted } - } else { - return fileChecksProgress - } }, 5000) } diff --git a/test/controllers/FileChecksControllerSpec.scala b/test/controllers/FileChecksControllerSpec.scala index 2f213766e..e69de29bb 100644 --- a/test/controllers/FileChecksControllerSpec.scala +++ b/test/controllers/FileChecksControllerSpec.scala @@ -1,559 +0,0 @@ -package controllers - -import com.github.tomakehurst.wiremock.WireMockServer -import com.github.tomakehurst.wiremock.client.WireMock._ -import configuration.GraphQLConfiguration -import graphql.codegen.GetConsignmentStatus.getConsignmentStatus.GetConsignment.ConsignmentStatuses -import graphql.codegen.GetFileCheckProgress.{getFileCheckProgress => fileCheck} -import io.circe.Printer -import io.circe.generic.auto._ -import io.circe.syntax._ -import org.mockito.ArgumentMatchers.anyString -import org.mockito.Mockito.when -import org.scalatest.concurrent.ScalaFutures.convertScalaFuture -import org.scalatest.prop.{TableDrivenPropertyChecks, TableFor1} -import play.api.test.CSRFTokenHelper.CSRFRequest -import play.api.test.FakeRequest -import play.api.test.Helpers.{status => playStatus, _} -import play.api.test.WsTestClient.InternalWSClient -import services.Statuses.{CompletedValue, CompletedWithIssuesValue, UploadType} -import services.{BackendChecksService, ConsignmentService, ConsignmentStatusService} -import testUtils.{CheckPageForStaticElements, FrontEndTestHelper} - -import java.time.{LocalDateTime, ZoneId, ZonedDateTime} -import java.util.UUID -import scala.collection.immutable.TreeMap -import scala.concurrent.{ExecutionContext, Future} -import scala.jdk.CollectionConverters.ListHasAsScala - -class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenPropertyChecks { - implicit val ec: ExecutionContext = ExecutionContext.global - - val totalFiles: Int = 40 - val consignmentId: UUID = UUID.fromString("b5bbe4d6-01a7-4305-99ef-9fce4a67917a") - val someDateTime: ZonedDateTime = ZonedDateTime.of(LocalDateTime.of(2022, 3, 10, 1, 0), ZoneId.systemDefault()) - - val wiremockServer = new WireMockServer(9006) - - override def beforeEach(): Unit = { - wiremockServer.start() - } - - override def afterEach(): Unit = { - wiremockServer.resetAll() - wiremockServer.stop() - } - - val fileChecks: TableFor1[String] = Table( - "User type", - "judgment", - "standard" - ) - - val checkPageForStaticElements = new CheckPageForStaticElements - - forAll(fileChecks) { userType => - "FileChecksController GET" should { - val (pathName, keycloakConfiguration, expectedTitle, expectedHeading, expectedInstruction, expectedForm) = if (userType == "judgment") { - ( - "judgment", - getValidJudgmentUserKeycloakConfiguration, - "Checking your upload - Transfer Digital Records - GOV.UK", - """

Checking your upload""", - """

Your judgment is being checked for errors. - | This may take a few minutes. Once your judgment has been checked, you will be redirected automatically. - |

""".stripMargin, - s"""
""".stripMargin - ) - } else { - ( - "consignment", - getValidStandardUserKeycloakConfiguration, - "Checking your records - Transfer Digital Records - GOV.UK", - """

Checking your records

""", - """

Please wait while your records are being checked. This may take a few minutes.

- |

The following checks are now being performed:

- |
    - |
  • Anti-virus scanning
  • - |
  • Identifying file formats
  • - |
  • Validating data integrity
  • - |
""".stripMargin, - s""" - | - |

- | This button will be enabled when we have finished checking your files. - |

- |
""".stripMargin - ) - } - - s"render the $userType fileChecks page if the checks are incomplete" in { - val graphQLConfiguration = new GraphQLConfiguration(app.configuration) - val consignmentService = new ConsignmentService(graphQLConfiguration) - val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) - val backendChecksService = new BackendChecksService(new InternalWSClient("http", 9007), app.configuration) - val filesProcessedWithAntivirus = 6 - - val filesProcessedWithChecksum = 12 - val filesProcessedWithFFID = 8 - val dataString: String = progressData(filesProcessedWithAntivirus, filesProcessedWithChecksum, filesProcessedWithFFID, allChecksSucceeded = false) - - val uploadStatus = List(ConsignmentStatuses(UUID.randomUUID(), UUID.randomUUID(), UploadType.id, CompletedValue.value, someDateTime, None)) - mockGetFileCheckProgress(dataString, userType) - setConsignmentReferenceResponse(wiremockServer) - setConsignmentStatusResponse(app.configuration, wiremockServer, consignmentStatuses = uploadStatus) - - val fileChecksController = new FileChecksController( - getAuthorisedSecurityComponents, - graphQLConfiguration, - keycloakConfiguration, - consignmentService, - frontEndInfoConfiguration, - backendChecksService, - consignmentStatusService - ) - val uploadFailed = "false" - val fileChecksPage = if (userType == "judgment") { - fileChecksController - .judgmentFileChecksPage(consignmentId, Some(uploadFailed)) - .apply(FakeRequest(GET, s"/$pathName/$consignmentId/file-checks?uploadFailed=$uploadFailed").withCSRFToken) - } else { - fileChecksController - .fileChecksPage(consignmentId, Some(uploadFailed)) - .apply(FakeRequest(GET, s"/$pathName/$consignmentId/file-checks?uploadFailed=$uploadFailed").withCSRFToken) - } - - val fileChecksPageAsString = contentAsString(fileChecksPage) - - playStatus(fileChecksPage) mustBe OK - contentType(fileChecksPage) mustBe Some("text/html") - headers(fileChecksPage) mustBe TreeMap("Cache-Control" -> "no-store, must-revalidate") - - checkPageForStaticElements.checkContentOfPagesThatUseMainScala(fileChecksPageAsString, userType = userType) - fileChecksPageAsString must include(expectedTitle) - fileChecksPageAsString must include(expectedHeading) - fileChecksPageAsString must include(expectedInstruction) - if (userType == "judgment") { - fileChecksPageAsString must include("""""") - } else { - fileChecksPageAsString must include( - """

For more information on these checks, please see our - | FAQ (opens in new tab) for this service. - |

""".stripMargin - ) - fileChecksPageAsString must include( - """
- |

- | Important - |

- |
- |
- |

Your records have been checked

- |

Please click 'Continue' to see your results.

- |
""".stripMargin - ) - } - fileChecksPageAsString must include(expectedForm) - } - - s"return a redirect to the auth server with an unauthenticated $userType user" in { - val graphQLConfiguration = new GraphQLConfiguration(app.configuration) - val consignmentService = new ConsignmentService(graphQLConfiguration) - val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) - val backendChecksService = new BackendChecksService(new InternalWSClient("http", 9007), app.configuration) - val controller = - new FileChecksController( - getUnauthorisedSecurityComponents, - graphQLConfiguration, - getValidKeycloakConfiguration, - consignmentService, - frontEndInfoConfiguration, - backendChecksService, - consignmentStatusService - ) - val fileChecksPage = controller.fileChecksPage(consignmentId, None).apply(FakeRequest(GET, s"/$pathName/$consignmentId/file-checks")) - - playStatus(fileChecksPage) mustBe FOUND - redirectLocation(fileChecksPage).get must startWith("/auth/realms/tdr/protocol/openid-connect/auth") - } - - s"render the $userType file checks complete page if the file checks are complete and all checks are successful" in { - val graphQLConfiguration = new GraphQLConfiguration(app.configuration) - val consignmentService = new ConsignmentService(graphQLConfiguration) - val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) - val backendChecksService = mock[BackendChecksService] - val dataString: String = progressData(40, 40, 40, allChecksSucceeded = true) - - val uploadStatus = List(ConsignmentStatuses(UUID.randomUUID(), UUID.randomUUID(), UploadType.id, InProgress.value, someDateTime, None)) - when(backendChecksService.triggerBackendChecks(org.mockito.ArgumentMatchers.any(classOf[UUID]), anyString())).thenReturn(Future.successful(true)) - mockGetFileCheckProgress(dataString, userType) - setUpdateConsignmentStatus(wiremockServer) - setConsignmentReferenceResponse(wiremockServer) - setConsignmentStatusResponse(app.configuration, wiremockServer, consignmentStatuses = uploadStatus) - - val fileChecksController = new FileChecksController( - getAuthorisedSecurityComponents, - graphQLConfiguration, - keycloakConfiguration, - consignmentService, - frontEndInfoConfiguration, - backendChecksService, - consignmentStatusService - ) - val uploadFailed = "false" - val fileChecksPage = if (userType == "judgment") { - fileChecksController - .judgmentFileChecksPage(consignmentId, Some(uploadFailed)) - .apply(FakeRequest(GET, s"/$pathName/$consignmentId/file-checks?uploadFailed=$uploadFailed").withCSRFToken) - } else { - fileChecksController - .fileChecksPage(consignmentId, Some(uploadFailed)) - .apply(FakeRequest(GET, s"/$pathName/$consignmentId/file-checks?uploadFailed=$uploadFailed").withCSRFToken) - } - - val fileChecksCompletePageAsString = contentAsString(fileChecksPage) - - playStatus(fileChecksPage) mustBe OK - - wiremockServer.verify(postRequestedFor(urlEqualTo("/graphql")).withRequestBody(containing("updateConsignmentStatus"))) - checkPageForStaticElements.checkContentOfPagesThatUseMainScala(fileChecksCompletePageAsString, userType = userType) - fileChecksCompletePageAsString must include(expectedTitle) - fileChecksCompletePageAsString must include(expectedHeading) - fileChecksCompletePageAsString must include("Your upload and checks have been completed.") - fileChecksCompletePageAsString must not include ( - s""" - Continue""" - ) - } - - s"render the $userType file checks complete page if the file checks are complete and all checks are not successful" in { - val graphQLConfiguration = new GraphQLConfiguration(app.configuration) - val consignmentService = new ConsignmentService(graphQLConfiguration) - val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) - val backendChecksService = new BackendChecksService(new InternalWSClient("http", 9007), app.configuration) - val dataString: String = progressData(40, 40, 40, allChecksSucceeded = false) - val uploadStatus = List(ConsignmentStatuses(UUID.randomUUID(), UUID.randomUUID(), UploadType.id, CompletedValue.value, someDateTime, None)) - - mockGetFileCheckProgress(dataString, userType) - setConsignmentStatusResponse(app.configuration, wiremockServer, consignmentStatuses = uploadStatus) - setConsignmentReferenceResponse(wiremockServer) - - val controller = new FileChecksController( - getAuthorisedSecurityComponents, - graphQLConfiguration, - keycloakConfiguration, - consignmentService, - frontEndInfoConfiguration, - backendChecksService, - consignmentStatusService - ) - val fileChecksCompletePage = if (userType == "judgment") { - controller.judgmentFileChecksPage(consignmentId, None).apply(FakeRequest(GET, s"/$pathName/$consignmentId/file-checks")) - } else { - controller.fileChecksPage(consignmentId, None).apply(FakeRequest(GET, s"/$pathName/$consignmentId/file-checks")) - } - val fileChecksCompletePageAsString = contentAsString(fileChecksCompletePage) - - checkPageForStaticElements.checkContentOfPagesThatUseMainScala(fileChecksCompletePageAsString, userType = userType) - playStatus(fileChecksCompletePage) mustBe OK - fileChecksCompletePageAsString must include(expectedTitle) - fileChecksCompletePageAsString must include(expectedHeading) - fileChecksCompletePageAsString must include("Your upload and checks have been completed.") - fileChecksCompletePageAsString must not include ( - s""" - Continue""" - ) - } - - s"render the $userType error page if the user upload fails and ensure consignment status is updated" in { - val graphQLConfiguration = new GraphQLConfiguration(app.configuration) - val consignmentService = new ConsignmentService(graphQLConfiguration) - val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) - val backendChecksService = new BackendChecksService(new InternalWSClient("http", 9007), app.configuration) - val filesProcessedWithAntivirus = 6 - - val filesProcessedWithChecksum = 12 - val filesProcessedWithFFID = 8 - val dataString: String = progressData(filesProcessedWithAntivirus, filesProcessedWithChecksum, filesProcessedWithFFID, allChecksSucceeded = false) - - val uploadStatus = List(ConsignmentStatuses(UUID.randomUUID(), UUID.randomUUID(), UploadType.id, CompletedValue.value, someDateTime, None)) - mockGetFileCheckProgress(dataString, userType) - setUpdateConsignmentStatus(wiremockServer) - setConsignmentReferenceResponse(wiremockServer) - setConsignmentStatusResponse(app.configuration, wiremockServer, consignmentStatuses = uploadStatus) - - val fileChecksController = new FileChecksController( - getAuthorisedSecurityComponents, - graphQLConfiguration, - keycloakConfiguration, - consignmentService, - frontEndInfoConfiguration, - backendChecksService, - consignmentStatusService - ) - val uploadFailed = "true" - val fileChecksPage = if (userType == "judgment") { - fileChecksController - .judgmentFileChecksPage(consignmentId, Some(uploadFailed)) - .apply(FakeRequest(GET, s"/$pathName/$consignmentId/file-checks?uploadFailed=$uploadFailed").withCSRFToken) - } else { - fileChecksController - .fileChecksPage(consignmentId, Some(uploadFailed)) - .apply(FakeRequest(GET, s"/$pathName/$consignmentId/file-checks?uploadFailed=$uploadFailed").withCSRFToken) - } - - val fileChecksPageAsString = contentAsString(fileChecksPage) - - playStatus(fileChecksPage) mustBe OK - contentType(fileChecksPage) mustBe Some("text/html") - headers(fileChecksPage) mustBe TreeMap("Cache-Control" -> "no-store, must-revalidate") - - checkPageForStaticElements.checkContentOfPagesThatUseMainScala(fileChecksPageAsString, userType = userType) - - fileChecksPageAsString must include( - """

- | There is a problem - |

""".stripMargin - ) - fileChecksPageAsString must include("""

Your upload was interrupted and could not be completed.

""") - wiremockServer.verify(postRequestedFor(urlEqualTo("/graphql")).withRequestBody(containing("updateConsignmentStatus"))) - } - - s"render the $userType error page if the user upload succeeds but backendChecks fails to trigger" in { - val graphQLConfiguration = new GraphQLConfiguration(app.configuration) - val consignmentService = new ConsignmentService(graphQLConfiguration) - val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) - val backendChecksService = mock[BackendChecksService] - val filesProcessedWithAntivirus = 6 - - val filesProcessedWithChecksum = 12 - val filesProcessedWithFFID = 8 - val dataString: String = progressData(filesProcessedWithAntivirus, filesProcessedWithChecksum, filesProcessedWithFFID, allChecksSucceeded = false) - - val uploadStatus = List(ConsignmentStatuses(UUID.randomUUID(), UUID.randomUUID(), UploadType.id, InProgress.value, someDateTime, None)) - when(backendChecksService.triggerBackendChecks(org.mockito.ArgumentMatchers.any(classOf[UUID]), anyString())).thenReturn(Future.successful(false)) - mockGetFileCheckProgress(dataString, userType) - setUpdateConsignmentStatus(wiremockServer) - setConsignmentReferenceResponse(wiremockServer) - setConsignmentStatusResponse(app.configuration, wiremockServer, consignmentStatuses = uploadStatus) - - val fileChecksController = new FileChecksController( - getAuthorisedSecurityComponents, - graphQLConfiguration, - keycloakConfiguration, - consignmentService, - frontEndInfoConfiguration, - backendChecksService, - consignmentStatusService - ) - val uploadFailed = "false" - val fileChecksPage = if (userType == "judgment") { - fileChecksController - .judgmentFileChecksPage(consignmentId, Some(uploadFailed)) - .apply(FakeRequest(GET, s"/$pathName/$consignmentId/file-checks?uploadFailed=$uploadFailed").withCSRFToken) - } else { - fileChecksController - .fileChecksPage(consignmentId, Some(uploadFailed)) - .apply(FakeRequest(GET, s"/$pathName/$consignmentId/file-checks?uploadFailed=$uploadFailed").withCSRFToken) - } - - val fileChecksPageAsString = contentAsString(fileChecksPage) - - playStatus(fileChecksPage) mustBe OK - contentType(fileChecksPage) mustBe Some("text/html") - headers(fileChecksPage) mustBe TreeMap("Cache-Control" -> "no-store, must-revalidate") - - checkPageForStaticElements.checkContentOfPagesThatUseMainScala(fileChecksPageAsString, userType = userType) - - fileChecksPageAsString must include( - """

- | There is a problem - |

""".stripMargin - ) - fileChecksPageAsString must include("""

Your upload was interrupted and could not be completed.

""") - wiremockServer.verify(postRequestedFor(urlEqualTo("/graphql")).withRequestBody(containing("updateConsignmentStatus"))) - } - - s"render the $userType error page if the user upload status is 'CompletedWithIssues'" in { - val graphQLConfiguration = new GraphQLConfiguration(app.configuration) - val consignmentService = new ConsignmentService(graphQLConfiguration) - val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) - val backendChecksService = mock[BackendChecksService] - val filesProcessedWithAntivirus = 6 - - val filesProcessedWithChecksum = 12 - val filesProcessedWithFFID = 8 - val dataString: String = progressData(filesProcessedWithAntivirus, filesProcessedWithChecksum, filesProcessedWithFFID, allChecksSucceeded = false) - - val uploadStatus = List(ConsignmentStatuses(UUID.randomUUID(), UUID.randomUUID(), UploadType.id, CompletedWithIssuesValue.value, someDateTime, None)) - when(backendChecksService.triggerBackendChecks(org.mockito.ArgumentMatchers.any(classOf[UUID]), anyString())).thenReturn(Future.successful(false)) - mockGetFileCheckProgress(dataString, userType) - setUpdateConsignmentStatus(wiremockServer) - setConsignmentReferenceResponse(wiremockServer) - setConsignmentStatusResponse(app.configuration, wiremockServer, consignmentStatuses = uploadStatus) - - val fileChecksController = new FileChecksController( - getAuthorisedSecurityComponents, - graphQLConfiguration, - keycloakConfiguration, - consignmentService, - frontEndInfoConfiguration, - backendChecksService, - consignmentStatusService - ) - val uploadFailed = "false" - val fileChecksPage = if (userType == "judgment") { - fileChecksController - .judgmentFileChecksPage(consignmentId, Some(uploadFailed)) - .apply(FakeRequest(GET, s"/$pathName/$consignmentId/file-checks?uploadFailed=$uploadFailed").withCSRFToken) - } else { - fileChecksController - .fileChecksPage(consignmentId, Some(uploadFailed)) - .apply(FakeRequest(GET, s"/$pathName/$consignmentId/file-checks?uploadFailed=$uploadFailed").withCSRFToken) - } - - val fileChecksPageAsString = contentAsString(fileChecksPage) - - playStatus(fileChecksPage) mustBe OK - contentType(fileChecksPage) mustBe Some("text/html") - headers(fileChecksPage) mustBe TreeMap("Cache-Control" -> "no-store, must-revalidate") - - checkPageForStaticElements.checkContentOfPagesThatUseMainScala(fileChecksPageAsString, userType = userType) - - fileChecksPageAsString must include( - """

- | There is a problem - |

""".stripMargin - ) - fileChecksPageAsString must include("""

Your upload was interrupted and could not be completed.

""") - wiremockServer.verify(0, postRequestedFor(urlEqualTo("/graphql")).withRequestBody(containing("updateConsignmentStatus"))) - wiremockServer.verify(0, postRequestedFor(urlEqualTo("/graphql")).withRequestBody(containing("getFileCheckProgress"))) - } - } - } - - "FileChecksController fileCheckProgress" should { - "call the fileCheckProgress endpoint" in { - val graphQLConfiguration: GraphQLConfiguration = new GraphQLConfiguration(app.configuration) - val consignmentService: ConsignmentService = new ConsignmentService(graphQLConfiguration) - val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) - val backendChecksService = new BackendChecksService(new InternalWSClient("http", 9007), app.configuration) - val controller = new FileChecksController( - getAuthorisedSecurityComponents, - graphQLConfiguration, - getValidStandardUserKeycloakConfiguration, - consignmentService, - frontEndInfoConfiguration, - backendChecksService, - consignmentStatusService - ) - - mockGetFileCheckProgress(progressData(1, 1, 1, allChecksSucceeded = true), "standard") - - val fileChecksResults = controller - .fileCheckProgress(consignmentId) - .apply(FakeRequest(POST, s"/consignment/$consignmentId/file-check-progress").withCSRFToken) - - playStatus(fileChecksResults) mustBe OK - - wiremockServer.getAllServeEvents.asScala.nonEmpty must be(true) - } - - "throw an error if the API returns an error" in { - val graphQLConfiguration: GraphQLConfiguration = new GraphQLConfiguration(app.configuration) - val consignmentService: ConsignmentService = new ConsignmentService(graphQLConfiguration) - val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) - val backendChecksService = new BackendChecksService(new InternalWSClient("http", 9007), app.configuration) - val controller = new FileChecksController( - getAuthorisedSecurityComponents, - graphQLConfiguration, - getValidStandardUserKeycloakConfiguration, - consignmentService, - frontEndInfoConfiguration, - backendChecksService, - consignmentStatusService - ) - - wiremockServer.stubFor( - post(urlEqualTo("/graphql")) - .withRequestBody(containing("getFileCheckProgress")) - .willReturn(serverError()) - ) - - val saveMetadataResponse = controller - .fileCheckProgress(consignmentId) - .apply(FakeRequest(POST, s"/consignment/$consignmentId/file-check-progress").withCSRFToken) - - val exception: Throwable = saveMetadataResponse.failed.futureValue - exception.getMessage must startWith("Unexpected response from GraphQL API") - } - } - - private def mockGetFileCheckProgress(dataString: String, userType: String) = { - setConsignmentTypeResponse(wiremockServer, userType) - wiremockServer.stubFor( - post(urlEqualTo("/graphql")) - .withRequestBody(containing("getFileCheckProgress")) - .willReturn(okJson(dataString)) - ) - } - - forAll(userChecks) { (user, url) => - s"The $url upload page" should { - s"return 403 if the url doesn't match the consignment type" in { - val graphQLConfiguration = new GraphQLConfiguration(app.configuration) - val consignmentService = new ConsignmentService(graphQLConfiguration) - val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) - val backendChecksService = new BackendChecksService(new InternalWSClient("http", 9007), app.configuration) - - val controller = new FileChecksController( - getAuthorisedSecurityComponents, - graphQLConfiguration, - user, - consignmentService, - frontEndInfoConfiguration, - backendChecksService, - consignmentStatusService - ) - - val fileChecksPage = url match { - case "judgment" => - setConsignmentTypeResponse(wiremockServer, "standard") - controller - .judgmentFileChecksPage(consignmentId, None) - .apply(FakeRequest(GET, s"/judgment/$consignmentId/file-checks")) - case "consignment" => - setConsignmentTypeResponse(wiremockServer, "judgment") - controller - .fileChecksPage(consignmentId, None) - .apply(FakeRequest(GET, s"/consignment/$consignmentId/file-checks")) - } - playStatus(fileChecksPage) mustBe FORBIDDEN - } - } - } - - private def progressData(filesProcessedWithAntivirus: Int, filesProcessedWithChecksum: Int, filesProcessedWithFFID: Int, allChecksSucceeded: Boolean): String = { - val client = new GraphQLConfiguration(app.configuration).getClient[fileCheck.Data, fileCheck.Variables]() - val antivirusProgress = fileCheck.GetConsignment.FileChecks.AntivirusProgress(filesProcessedWithAntivirus) - val checksumProgress = fileCheck.GetConsignment.FileChecks.ChecksumProgress(filesProcessedWithChecksum) - val ffidProgress = fileCheck.GetConsignment.FileChecks.FfidProgress(filesProcessedWithFFID) - val fileChecks = fileCheck.GetConsignment.FileChecks(antivirusProgress, checksumProgress, ffidProgress) - val fileStatus = List(fileCheck.GetConsignment.Files(Some("Success"))) - val data: client.GraphqlData = client.GraphqlData( - Some( - fileCheck.Data( - Some( - fileCheck.GetConsignment(allChecksSucceeded, Option(""), totalFiles, fileStatus, fileChecks) - ) - ) - ) - ) - val dataString: String = data.asJson.printWith(Printer(dropNullValues = false, "")) - dataString - } -}