diff --git a/app/uk/gov/hmrc/formpproxy/cis/controllers/VerificationController.scala b/app/uk/gov/hmrc/formpproxy/cis/controllers/VerificationController.scala index fde23076..d821d4f1 100644 --- a/app/uk/gov/hmrc/formpproxy/cis/controllers/VerificationController.scala +++ b/app/uk/gov/hmrc/formpproxy/cis/controllers/VerificationController.scala @@ -18,7 +18,7 @@ package uk.gov.hmrc.formpproxy.cis.controllers import play.api.Logging import play.api.libs.json.{JsError, JsValue, Json} -import play.api.mvc.{Action, AnyContent, ControllerComponents} +import play.api.mvc.{Action, AnyContent, ControllerComponents, Result} import uk.gov.hmrc.formpproxy.actions.AuthAction import uk.gov.hmrc.formpproxy.cis.services.VerificationService import uk.gov.hmrc.play.bootstrap.backend.controller.BackendController @@ -26,6 +26,7 @@ import uk.gov.hmrc.formpproxy.cis.models.requests._ import javax.inject.Inject import scala.concurrent.{ExecutionContext, Future} +import scala.util.control.NonFatal class VerificationController @Inject() ( authorise: AuthAction, @@ -57,24 +58,6 @@ class VerificationController @Inject() ( } } - def getSubmissionWithVerificationBatch: Action[JsValue] = - Action(parse.json).async { implicit request => - request.body - .validate[GetSubmissionWithVerificationBatchRequest] - .fold( - errs => - Future.successful(BadRequest(Json.obj("message" -> "Invalid payload", "errors" -> JsError.toJson(errs)))), - req => - service - .getSubmissionWithVerificationBatch(req) - .map(res => Ok(Json.toJson(res))) - .recover { case t => - logger.error("[getSubmissionWithVerificationBatch] failed", t) - InternalServerError(Json.obj("message" -> "Unexpected error")) - } - ) - } - def createVerificationBatchAndVerifications(): Action[JsValue] = authorise(parse.json).async { implicit request => request.body @@ -164,6 +147,53 @@ class VerificationController @Inject() ( } ) } + def getSubmissionWithVerificationBatchByRefs( + instanceId: String, + verificationBatchResourceRef: Long + ): Action[AnyContent] = + authorise.async { implicit request => + handleGetSubmissionWithVerificationBatch( + GetSubmissionWithVerificationBatchRequest( + instanceId = instanceId, + verificationBatchResourceRef = verificationBatchResourceRef + ) + ) + } + + def getSubmissionWithVerificationBatch: Action[JsValue] = + Action(parse.json).async { implicit request => + request.body + .validate[GetSubmissionWithVerificationBatchRequest] + .fold( + errors => + Future.successful( + BadRequest( + Json.obj( + "message" -> "Invalid payload", + "errors" -> JsError.toJson(errors) + ) + ) + ), + handleGetSubmissionWithVerificationBatch + ) + } + + private def handleGetSubmissionWithVerificationBatch( + request: GetSubmissionWithVerificationBatchRequest + ): Future[Result] = + service + .getSubmissionWithVerificationBatch(request) + .map(response => Ok(Json.toJson(response))) + .recover { case NonFatal(exception) => + logger.error( + s"[VerificationController][getSubmissionWithVerificationBatch] failed for " + + s"instanceId=${request.instanceId}, " + + s"verificationBatchResourceRef=${request.verificationBatchResourceRef}", + exception + ) + + InternalServerError(Json.obj("message" -> "Unexpected error")) + } def getSubmittedVerifications(): Action[JsValue] = authorise(parse.json).async { implicit request => diff --git a/app/uk/gov/hmrc/formpproxy/sdlt/repositories/SdltFormpRepository.scala b/app/uk/gov/hmrc/formpproxy/sdlt/repositories/SdltFormpRepository.scala index d8d20ed5..dcb2f00e 100644 --- a/app/uk/gov/hmrc/formpproxy/sdlt/repositories/SdltFormpRepository.scala +++ b/app/uk/gov/hmrc/formpproxy/sdlt/repositories/SdltFormpRepository.scala @@ -2586,8 +2586,6 @@ class SdltFormpRepository @Inject() (@NamedDatabase("sdlt") db: Database)(implic cs.execute() - val submissionId = cs.getLong(4) - CreateSubmissionReturn(success = true) } finally cs.close() } @@ -2696,7 +2694,6 @@ class SdltFormpRepository @Inject() (@NamedDatabase("sdlt") db: Database)(implic cs.execute() - val errorDetailId = cs.getLong(5) CreateSubmissionErrorDetailReturn(success = true) } finally cs.close() } diff --git a/conf/app.routes b/conf/app.routes index 2f608bbd..2b20840b 100644 --- a/conf/app.routes +++ b/conf/app.routes @@ -52,6 +52,7 @@ POST /cis/amend-monthly-return/create uk.gov.hmrc. POST /cis/verification/submission/create uk.gov.hmrc.formpproxy.cis.controllers.VerificationController.createSubmissionAndUpdateVerifications() POST /cis/verification/submitted-verifications uk.gov.hmrc.formpproxy.cis.controllers.VerificationController.getSubmittedVerifications() POST /cis/verification/submission/update uk.gov.hmrc.formpproxy.cis.controllers.VerificationController.updateVerificationSubmission() +GET /cis/verification/submission-batch/:instanceId/:verificationBatchResourceRef uk.gov.hmrc.formpproxy.cis.controllers.VerificationController.getSubmissionWithVerificationBatchByRefs(instanceId: String, verificationBatchResourceRef: Long) GET /cis/batchpoll-submissions uk.gov.hmrc.formpproxy.cis.controllers.BatchPollController.getBatchPollSubmissions() diff --git a/test/uk/gov/hmrc/formpproxy/cis/controllers/VerificationControllerSpec.scala b/test/uk/gov/hmrc/formpproxy/cis/controllers/VerificationControllerSpec.scala index 645cf9e6..d6c8731a 100644 --- a/test/uk/gov/hmrc/formpproxy/cis/controllers/VerificationControllerSpec.scala +++ b/test/uk/gov/hmrc/formpproxy/cis/controllers/VerificationControllerSpec.scala @@ -1099,26 +1099,90 @@ class VerificationControllerSpec extends SpecBase { verifyNoMoreInteractions(mockService) } - "returns 400 BadRequest with error payload when JSON is invalid" in { - val s = setup - import s.* + "GET /cis/verification/submission-batch/:instanceId/:verificationBatchResourceRef (getSubmissionWithVerificationBatchByRefs)" - { - val badJson = Json.obj("wrong" -> "value") + "returns 200 OK with JSON body when service succeeds" in { + val s = setup + import s.* - val req = FakeRequest(POST, url) - .withHeaders(CONTENT_TYPE -> JSON) - .withBody(badJson) + val requestModel = + GetSubmissionWithVerificationBatchRequest( + instanceId = "abc-123", + verificationBatchResourceRef = 77L + ) - val result = controller.getSubmittedVerifications().apply(req) + val responseModel = + GetSubmissionWithVerificationBatchResponse( + scheme = None, + subcontractors = Seq.empty, + verifications = Seq.empty, + verificationBatch = None, + submission = None + ) - status(result) mustBe BAD_REQUEST - contentType(result) mustBe Some(JSON) + when(mockService.getSubmissionWithVerificationBatch(eqTo(requestModel))) + .thenReturn(Future.successful(responseModel)) - val body = contentAsJson(result) - (body \ "message").as[String] mustBe "Invalid payload" - (body \ "errors").isDefined mustBe true + val req = + FakeRequest( + GET, + s"/cis/verification/submission-batch/${requestModel.instanceId}/${requestModel.verificationBatchResourceRef}" + ) - verifyNoInteractions(mockService) + val result = + controller + .getSubmissionWithVerificationBatchByRefs( + requestModel.instanceId, + requestModel.verificationBatchResourceRef + ) + .apply(req) + + status(result) mustBe OK + contentType(result) mustBe Some(JSON) + contentAsJson(result) mustBe Json.toJson(responseModel) + + verify(mockService) + .getSubmissionWithVerificationBatch(eqTo(requestModel)) + + verifyNoMoreInteractions(mockService) + } + + "returns 500 InternalServerError with error body when service fails" in { + val s = setup + import s.* + + val requestModel = + GetSubmissionWithVerificationBatchRequest( + instanceId = "abc-123", + verificationBatchResourceRef = 77L + ) + + when(mockService.getSubmissionWithVerificationBatch(eqTo(requestModel))) + .thenReturn(Future.failed(new RuntimeException("boom"))) + + val req = + FakeRequest( + GET, + s"/cis/verification/submission-batch/${requestModel.instanceId}/${requestModel.verificationBatchResourceRef}" + ) + + val result = + controller + .getSubmissionWithVerificationBatchByRefs( + requestModel.instanceId, + requestModel.verificationBatchResourceRef + ) + .apply(req) + + status(result) mustBe INTERNAL_SERVER_ERROR + contentType(result) mustBe Some(JSON) + contentAsJson(result) mustBe Json.obj("message" -> "Unexpected error") + + verify(mockService) + .getSubmissionWithVerificationBatch(eqTo(requestModel)) + + verifyNoMoreInteractions(mockService) + } } "returns 500 InternalServerError with error body when service fails" in { diff --git a/test/uk/gov/hmrc/formpproxy/cis/repositories/CisFormpRepositorySpec.scala b/test/uk/gov/hmrc/formpproxy/cis/repositories/CisFormpRepositorySpec.scala index 324a5a40..0df2d79a 100644 --- a/test/uk/gov/hmrc/formpproxy/cis/repositories/CisFormpRepositorySpec.scala +++ b/test/uk/gov/hmrc/formpproxy/cis/repositories/CisFormpRepositorySpec.scala @@ -26,10 +26,9 @@ import play.api.db.Database import uk.gov.hmrc.formpproxy.base.SpecBase import uk.gov.hmrc.formpproxy.cis.models.* import uk.gov.hmrc.formpproxy.cis.models.requests.* -import uk.gov.hmrc.formpproxy.cis.repositories.CisStoredProcedures.CallDeleteSubcontractor import uk.gov.hmrc.formpproxy.cis.models.response.* +import uk.gov.hmrc.formpproxy.cis.repositories.CisStoredProcedures.CallDeleteSubcontractor import uk.gov.hmrc.formpproxy.shared.utils.CallableStatementUtils.* -import uk.gov.hmrc.formpproxy.cis.models.response.GetSubcontractorResponse import java.sql.* import java.time.{Instant, LocalDateTime} @@ -3407,7 +3406,6 @@ final class CisFormpRepositorySpec extends SpecBase { verify(csUpdateV2).close() } } - "getBatchPollSubmissions" - { "register both cursors, execute the proc, and assemble the response" in { val db = mock[Database] @@ -3751,6 +3749,67 @@ final class CisFormpRepositorySpec extends SpecBase { verify(csUpdateSubmission).execute() } + "getSubmissionWithVerificationBatch" - { + + "call Get_Verif_Batch_Submission and return response using existing helper mapping" in { + val db = mock[Database] + val conn = mock[Connection] + val cs = mock[CallableStatement] + + val submissionRs = mock[ResultSet] + val verificationBatchRs = mock[ResultSet] + val verificationsRs = mock[ResultSet] + val subcontractorsRs = mock[ResultSet] + val schemeRs = mock[ResultSet] + + val request = GetSubmissionWithVerificationBatchRequest( + instanceId = "abc-123", + verificationBatchResourceRef = 77L + ) + + when(db.withConnection(anyArg[Connection => Any])).thenAnswer { inv => + val f = inv.getArgument(0, classOf[Connection => Any]) + f(conn) + } + + when(conn.prepareCall("{ call SUBMISSION_PROCS.Get_Verif_Batch_Submission(?, ?, ?, ?, ?, ?, ?) }")) + .thenReturn(cs) + + when(cs.getObject(3, classOf[ResultSet])).thenReturn(submissionRs) + when(cs.getObject(4, classOf[ResultSet])).thenReturn(verificationBatchRs) + when(cs.getObject(5, classOf[ResultSet])).thenReturn(verificationsRs) + when(cs.getObject(6, classOf[ResultSet])).thenReturn(subcontractorsRs) + when(cs.getObject(7, classOf[ResultSet])).thenReturn(schemeRs) + + when(submissionRs.next()).thenReturn(false) + when(verificationBatchRs.next()).thenReturn(false) + when(verificationsRs.next()).thenReturn(false) + when(subcontractorsRs.next()).thenReturn(false) + when(schemeRs.next()).thenReturn(false) + + val repo = new CisFormpRepository(db) + + val result = + repo.getSubmissionWithVerificationBatch(request).futureValue + + result.scheme mustBe None + result.subcontractors mustBe Seq.empty + result.verifications mustBe Seq.empty + result.verificationBatch mustBe None + result.submission mustBe None + + verify(conn).prepareCall("{ call SUBMISSION_PROCS.Get_Verif_Batch_Submission(?, ?, ?, ?, ?, ?, ?) }") + verify(cs).setString(1, request.instanceId) + verify(cs).setLong(2, request.verificationBatchResourceRef) + verify(cs).registerOutParameter(3, OracleTypes.CURSOR) + verify(cs).registerOutParameter(4, OracleTypes.CURSOR) + verify(cs).registerOutParameter(5, OracleTypes.CURSOR) + verify(cs).registerOutParameter(6, OracleTypes.CURSOR) + verify(cs).registerOutParameter(7, OracleTypes.CURSOR) + verify(cs).execute() + } + } + "throws when scheme is missing" in { val db = mock[Database] val conn = mock[Connection] diff --git a/test/uk/gov/hmrc/formpproxy/cis/services/VerificationServiceSpec.scala b/test/uk/gov/hmrc/formpproxy/cis/services/VerificationServiceSpec.scala index e2a0eeaa..4d8d09f1 100644 --- a/test/uk/gov/hmrc/formpproxy/cis/services/VerificationServiceSpec.scala +++ b/test/uk/gov/hmrc/formpproxy/cis/services/VerificationServiceSpec.scala @@ -496,6 +496,56 @@ class VerificationServiceSpec extends SpecBase { } } + "VerificationService#getSubmissionWithVerificationBatch" - { + + "return successful response from repo" in { + val c = Ctx() + import c.* + + val request = GetSubmissionWithVerificationBatchRequest( + instanceId = "abc-123", + verificationBatchResourceRef = 77L + ) + + val response = GetSubmissionWithVerificationBatchResponse( + scheme = None, + subcontractors = Seq.empty, + verifications = Seq.empty, + verificationBatch = None, + submission = None + ) + + when(repo.getSubmissionWithVerificationBatch(eqTo(request))) + .thenReturn(Future.successful(response)) + + service.getSubmissionWithVerificationBatch(request).futureValue mustBe response + + verify(repo).getSubmissionWithVerificationBatch(eqTo(request)) + verifyNoMoreInteractions(repo) + } + + "propagates failure from repo" in { + val c = Ctx() + import c.* + + val request = GetSubmissionWithVerificationBatchRequest( + instanceId = "abc-123", + verificationBatchResourceRef = 77L + ) + + val boom = new RuntimeException("boom") + + when(repo.getSubmissionWithVerificationBatch(eqTo(request))) + .thenReturn(Future.failed(boom)) + + val ex = service.getSubmissionWithVerificationBatch(request).failed.futureValue + + ex mustBe boom + + verify(repo).getSubmissionWithVerificationBatch(eqTo(request)) + verifyNoMoreInteractions(repo) + } + } "VerificationService#getSubmittedVerifications" - { "delegates to repository" in {