Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions app/controllers/LandingController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package controllers

import controllers.actions.IdentifierAction
import controllers.actions.{AllowListFilterAction, IdentifierAction}
import play.api.Logging
import play.api.i18n.I18nSupport
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
Expand All @@ -28,18 +28,20 @@ import scala.concurrent.ExecutionContext

class LandingController @Inject() (nddService: NationalDirectDebitService,
val controllerComponents: MessagesControllerComponents,
identify: IdentifierAction
identify: IdentifierAction,
allowListFilter: AllowListFilterAction
)(implicit ec: ExecutionContext)
extends FrontendBaseController
with I18nSupport
with Logging {

def onPageLoad(): Action[AnyContent] = identify.async { implicit request =>
nddService.retrieveAllDirectDebits(request.userId) map {
case rdsResponse if rdsResponse.directDebitCount == 0 =>
def onPageLoad(): Action[AnyContent] = (identify andThen allowListFilter).async { implicit request =>
nddService.retrieveAllDirectDebits(request.userId).map { rdsResponse =>
if (rdsResponse.directDebitCount == 0) {
Redirect(routes.SetupDirectDebitPaymentController.onPageLoad())
case response =>
} else {
Redirect(routes.YourDirectDebitInstructionsController.onPageLoad())
}
}
}
}
5 changes: 3 additions & 2 deletions app/controllers/SetupDirectDebitPaymentController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,13 @@ class SetupDirectDebitPaymentController @Inject() (
nddService: NationalDirectDebitService,
val controllerComponents: MessagesControllerComponents,
view: SetupDirectDebitPaymentView,
lockService: LockService
lockService: LockService,
allowListFilter: AllowListFilterAction
)(implicit ec: ExecutionContext)
extends FrontendBaseController
with I18nSupport {

def onPageLoad(): Action[AnyContent] = (identify andThen getData).async { implicit request =>
def onPageLoad(): Action[AnyContent] = (identify andThen allowListFilter andThen getData).async { implicit request =>
lockService.isUserLocked(request.userId) flatMap {
_.lockStatus match
case NotLocked =>
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/YourBankDetailsController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class YourBankDetailsController @Inject() (

personalOrBusinessOpt match {
case None =>
logger.warn(s"Missing PersonalOrBusinessAccountPage for user: $credId")
logger.warn(s"Missing PersonalOrBusinessAccountPage for user")
Future.successful(Redirect(routes.SystemErrorController.onPageLoad()))

case Some(accountType) =>
Expand Down
5 changes: 3 additions & 2 deletions app/controllers/YourDirectDebitInstructionsController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,13 @@ class YourDirectDebitInstructionsController @Inject() (
appConfig: FrontendAppConfig,
nddService: NationalDirectDebitService,
paginationService: PaginationService,
sessionRepository: SessionRepository
sessionRepository: SessionRepository,
allowListFilter: AllowListFilterAction
)(implicit ec: ExecutionContext)
extends FrontendBaseController
with I18nSupport {

def onPageLoad: Action[AnyContent] = (identify andThen getData).async { implicit request =>
def onPageLoad: Action[AnyContent] = (identify andThen allowListFilter andThen getData).async { implicit request =>
val userAnswers = request.userAnswers.getOrElse(UserAnswers(request.userId))
val currentPage = request.getQueryString("page").flatMap(_.toIntOption).getOrElse(1)

Expand Down
47 changes: 47 additions & 0 deletions app/controllers/actions/AllowListFilterAction.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2026 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package controllers.actions

import models.requests.IdentifierRequest
import play.api.mvc.{ActionFilter, Result}
import play.api.{Configuration, Logging}
import play.api.mvc.Results.SeeOther
import splitter.connectors.AllowListConnector
import uk.gov.hmrc.http.HeaderCarrier

import javax.inject.Inject
import scala.concurrent.{ExecutionContext, Future}
import scala.util.control.NonFatal

class AllowListFilterAction @Inject() (connector: AllowListConnector, configuration: Configuration)(implicit val executionContext: ExecutionContext)
extends ActionFilter[IdentifierRequest]
with Logging {

private val legacyStartUrl = configuration.get[String]("microservice.services.ndds-legacy.path")

override protected def filter[A](request: IdentifierRequest[A]): Future[Option[Result]] =
implicit val hc: HeaderCarrier = HeaderCarrier()
connector
.check(request.userId)
.map:
case true => None
case false => Some(SeeOther(legacyStartUrl))
.recover:
case NonFatal(e) =>
logger.error("Error when checking for user id ", e)
Some(SeeOther(legacyStartUrl))
}
2 changes: 1 addition & 1 deletion app/services/NationalDirectDebitService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ class NationalDirectDebitService @Inject() (nddConnector: NationalDirectDebitCon
userAnswers
.get(YourBankDetailsPage)
.map(_.auddisStatus)
.getOrElse(throw new Exception("YourBankDetailsPage details missing from user answers"))
.getOrElse(throw new Exception("Missing information from user answers"))
)
}
}
Expand Down
1 change: 0 additions & 1 deletion app/splitter/controllers/SplitterController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package splitter.controllers

import controllers.actions.IdentifierAction
import play.api.mvc.*
import play.api.{Configuration, Logging}
import splitter.connectors.AllowListConnector
Expand Down
2 changes: 1 addition & 1 deletion conf/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ features {
maxNumberPPsAllowed = 99
enableLockService = false
existingDirectDebit = true
allowListChecksEnabled = false
allowListChecksEnabled = true
}

mac {
Expand Down
55 changes: 42 additions & 13 deletions test/controllers/LandingControllerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,45 +22,56 @@ import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito.when
import org.scalatestplus.mockito.MockitoSugar.mock
import play.api.inject.bind
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.test.FakeRequest
import play.api.test.Helpers.*
import services.NationalDirectDebitService
import splitter.connectors.AllowListConnector
import splitter.controllers.{FakeIdentityIdentifierAction, IdentityIdentifierAction}
import uk.gov.hmrc.http.HeaderCarrier

import scala.concurrent.Future

class LandingControllerSpec extends SpecBase {

"Landing Controller" - {
class FakeAllowListConnector(result: Exception | Boolean) extends AllowListConnector:
override def check(userId: String)(using HeaderCarrier): Future[Boolean] =
result match
case res: Boolean => Future.successful(res)
case res: Exception => Future.failed(res)

private def legacyUrl = "/national-direct-debits"

"Landing Controller" - {
val mockService = mock[NationalDirectDebitService]

"must return REDIRECT and the correct view for a GET with no existing debits" in {

val application = applicationBuilder(userAnswers = None)
.overrides(
bind[NationalDirectDebitService].toInstance(mockService)
bind[NationalDirectDebitService].toInstance(mockService),
bind[IdentityIdentifierAction].to[FakeIdentityIdentifierAction],
bind[AllowListConnector].toInstance(FakeAllowListConnector(true))
)
.build()

running(application) {

when(mockService.retrieveAllDirectDebits(any())(any(), any()))
.thenReturn(Future.successful(NddResponse(0, Seq())))

val request = FakeRequest(GET, routes.LandingController.onPageLoad().url)

val result = route(application, request).value

status(result) mustEqual SEE_OTHER
redirectLocation(result).value must startWith(controllers.routes.SetupDirectDebitPaymentController.onPageLoad().url)
redirectLocation(result).value mustBe controllers.routes.SetupDirectDebitPaymentController.onPageLoad().url
}
}

"must return REDIRECT and the correct view for a GET with existing debits" in {

val application = applicationBuilder(userAnswers = None)
.overrides(
bind[NationalDirectDebitService].toInstance(mockService)
bind[NationalDirectDebitService].toInstance(mockService),
bind[IdentityIdentifierAction].to[FakeIdentityIdentifierAction],
bind[AllowListConnector].toInstance(FakeAllowListConnector(true))
)
.build()

Expand All @@ -70,30 +81,48 @@ class LandingControllerSpec extends SpecBase {
.thenReturn(Future.successful(NddResponse(2, Seq())))

val request = FakeRequest(GET, routes.LandingController.onPageLoad().url)

val result = route(application, request).value

status(result) mustEqual SEE_OTHER
redirectLocation(result).value must startWith(controllers.routes.YourDirectDebitInstructionsController.onPageLoad().url)
redirectLocation(result).value mustBe controllers.routes.YourDirectDebitInstructionsController.onPageLoad().url
}
}

"must return REDIRECT and the correct view for an unauthenticated user" in {

val application = applicationBuilder(userAnswers = None)
.overrides(
bind[NationalDirectDebitService].toInstance(mockService)
bind[NationalDirectDebitService].toInstance(mockService),
bind[IdentityIdentifierAction].to[FakeIdentityIdentifierAction],
bind[AllowListConnector].toInstance(FakeAllowListConnector(true))
)
.build()

running(application) {
val request = FakeRequest(GET, routes.LandingController.onPageLoad().url)
val result = route(application, request).value

status(result) mustEqual SEE_OTHER
redirectLocation(result).value mustBe controllers.routes.YourDirectDebitInstructionsController.onPageLoad().url
}
}

"must return REDIRECT to legacy url when check is false" in {
val application = applicationBuilder(userAnswers = None)
.overrides(
bind[NationalDirectDebitService].toInstance(mockService),
bind[IdentityIdentifierAction].to[FakeIdentityIdentifierAction],
bind[AllowListConnector].toInstance(FakeAllowListConnector(false))
)
.build()

running(application) {
val request = FakeRequest(GET, routes.LandingController.onPageLoad().url)
val result = route(application, request).value

status(result) mustEqual SEE_OTHER
redirectLocation(result).value must startWith(controllers.routes.YourDirectDebitInstructionsController.onPageLoad().url)
redirectLocation(result).value mustBe legacyUrl
}
}

}
}
19 changes: 14 additions & 5 deletions test/controllers/SetupDirectDebitPaymentControllerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import play.api.mvc.Call
import play.api.test.FakeRequest
import play.api.test.Helpers.*
import services.{LockService, NationalDirectDebitService}
import splitter.connectors.{AllowListConnector, FakeAllowListConnector}
import splitter.controllers.{FakeIdentityIdentifierAction, IdentityIdentifierAction}
import views.html.SetupDirectDebitPaymentView

import java.time.Instant
Expand All @@ -35,7 +37,6 @@ import scala.concurrent.Future
class SetupDirectDebitPaymentControllerSpec extends SpecBase {

"SetupDirectDebitPayment Controller" - {

val mockService = mock[LockService]
val returnedDate = "2025-06-28T15:30:30Z"

Expand All @@ -60,7 +61,9 @@ class SetupDirectDebitPaymentControllerSpec extends SpecBase {
val application = applicationBuilder(userAnswers = Some(emptyUserAnswers))
.overrides(
bind[NationalDirectDebitService].toInstance(mockRDSDatacacheService),
bind[LockService].toInstance(mockService)
bind[LockService].toInstance(mockService),
bind[IdentityIdentifierAction].to[FakeIdentityIdentifierAction],
bind[AllowListConnector].toInstance(FakeAllowListConnector(true))
)
.build()

Expand Down Expand Up @@ -89,7 +92,9 @@ class SetupDirectDebitPaymentControllerSpec extends SpecBase {
val application = applicationBuilder(userAnswers = Some(emptyUserAnswers))
.overrides(
bind[NationalDirectDebitService].toInstance(mockRDSDatacacheService),
bind[LockService].toInstance(mockService)
bind[LockService].toInstance(mockService),
bind[IdentityIdentifierAction].to[FakeIdentityIdentifierAction],
bind[AllowListConnector].toInstance(FakeAllowListConnector(true))
)
.build()

Expand Down Expand Up @@ -118,7 +123,9 @@ class SetupDirectDebitPaymentControllerSpec extends SpecBase {
"must return See Other and redirect for a GET if the user is locked but not verified" in {
val application = applicationBuilder(userAnswers = Some(emptyUserAnswers))
.overrides(
bind[LockService].toInstance(mockService)
bind[LockService].toInstance(mockService),
bind[IdentityIdentifierAction].to[FakeIdentityIdentifierAction],
bind[AllowListConnector].toInstance(FakeAllowListConnector(true))
)
.build()

Expand All @@ -139,7 +146,9 @@ class SetupDirectDebitPaymentControllerSpec extends SpecBase {
"must return See Other and redirect for a GET if the user is locked and verified" in {
val application = applicationBuilder(userAnswers = Some(emptyUserAnswers))
.overrides(
bind[LockService].toInstance(mockService)
bind[LockService].toInstance(mockService),
bind[IdentityIdentifierAction].to[FakeIdentityIdentifierAction],
bind[AllowListConnector].toInstance(FakeAllowListConnector(true))
)
.build()

Expand Down
Loading