Skip to content

Commit

Permalink
MMCA-5083 : Create new endpoint in data store for verified-email (#56)
Browse files Browse the repository at this point in the history
* MMCA-5083 : Initial commit

* MMCA-5803: PR comment fixes
  • Loading branch information
HariHmrc authored Jan 7, 2025
1 parent a2c6665 commit f3da262
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 22 deletions.
27 changes: 23 additions & 4 deletions app/controllers/VerifiedEmailController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package controllers

import actionbuilders.{AuthorisedRequest, RequestWithEori}
import cats.data.OptionT
import cats.implicits._
import connectors.Sub09Connector
Expand All @@ -33,6 +34,7 @@ import scala.concurrent.{ExecutionContext, Future}
class VerifiedEmailController @Inject() (
emailRepo: EmailRepository,
subscriptionInfoConnector: Sub09Connector,
authorisedRequest: AuthorisedRequest,
cc: ControllerComponents
)(implicit executionContext: ExecutionContext)
extends BackendController(cc) {
Expand All @@ -41,10 +43,7 @@ class VerifiedEmailController @Inject() (
def retrieveAndStoreEmail: Future[Result] =
(for {
notificationEmail <- OptionT(subscriptionInfoConnector.getSubscriberInformation(eori))
result <- OptionT.liftF(emailRepo.set(eori, notificationEmail).map {
case SuccessfulEmail => Ok(Json.toJson(notificationEmail))
case _ => InternalServerError
})
result <- OptionT.liftF(storeEmail(eori, notificationEmail))
} yield result).getOrElse(NotFound)

emailRepo.get(eori).flatMap {
Expand All @@ -53,6 +52,14 @@ class VerifiedEmailController @Inject() (
}
}

def getVerifiedEmailV2: Action[AnyContent] = authorisedRequest async {
implicit request: RequestWithEori[AnyContent] =>
emailRepo.get(request.eori.value).flatMap {
case Some(value) => Future.successful(Ok(Json.toJson(value)))
case None => retrieveAndStoreEmail(request.eori.value)
}
}

def updateVerifiedEmail(): Action[UpdateVerifiedEmailRequest] =
Action.async(parse.json[UpdateVerifiedEmailRequest]) { implicit request =>
emailRepo
Expand All @@ -65,4 +72,16 @@ class VerifiedEmailController @Inject() (
case _ => InternalServerError
}
}

private def retrieveAndStoreEmail(eori: String): Future[Result] =
(for {
notificationEmail <- OptionT(subscriptionInfoConnector.getSubscriberInformation(eori))
result <- OptionT.liftF(storeEmail(eori, notificationEmail))
} yield result).getOrElse(NotFound)

private def storeEmail(eori: String, notificationEmail: NotificationEmail): Future[Result] =
emailRepo.set(eori, notificationEmail).map {
case SuccessfulEmail => Ok(Json.toJson(notificationEmail))
case _ => InternalServerError
}
}
2 changes: 2 additions & 0 deletions conf/app.routes
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# microservice specific routes

GET /eori/:eori/verified-email controllers.VerifiedEmailController.getVerifiedEmail(eori: String)
GET /eori/verified-email controllers.VerifiedEmailController.getVerifiedEmailV2()

GET /eori/:eori/company-information controllers.CompanyInformationController.getCompanyInformation(eori: String)
GET /eori/:eori/eori-history controllers.EoriHistoryController.getEoriHistory(eori: String)
GET /eori/:eori/xieori-information controllers.XiEoriController.getXiEoriInformation(eori: String)
Expand Down
114 changes: 96 additions & 18 deletions test/controllers/VerifiedEmailControllerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package controllers

import actionbuilders.CustomAuthConnector
import config.Platform.{ENROLMENT_IDENTIFIER, ENROLMENT_KEY}
import connectors.Sub09Connector
import models.repositories.{FailedToRetrieveEmail, SuccessfulEmail}
import models.{NotificationEmail, TraderData}
Expand All @@ -27,8 +29,9 @@ import play.api.{Application, inject}
import play.api.libs.json.Json
import play.api.mvc.{AnyContentAsEmpty, AnyContentAsJson}
import play.api.test.FakeRequest
import play.api.test.Helpers._
import play.api.test.Helpers.*
import repositories.EmailRepository
import uk.gov.hmrc.auth.core.{Enrolment, EnrolmentIdentifier, Enrolments}
import utils.SpecBase

import java.time.LocalDate
Expand All @@ -43,11 +46,10 @@ class VerifiedEmailControllerSpec extends SpecBase {
when(mockEmailRepository.get(any())).thenReturn(Future.successful(None))
when(mockSubscriptionInfoService.getSubscriberInformation(any())).thenReturn(Future.successful(None))

val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, getRoute)
val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, getVerifiedEmailRoute)

running(app) {
val result = route(app, request).value

status(result) mustBe NOT_FOUND
}
}
Expand All @@ -59,15 +61,12 @@ class VerifiedEmailControllerSpec extends SpecBase {
when(mockEmailRepository.set(any(), any())).thenReturn(Future.successful(SuccessfulEmail))
when(mockSubscriptionInfoService.getSubscriberInformation(any())).thenReturn(Future.successful(None))

val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, getRoute)
val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, getVerifiedEmailRoute)

running(app) {
val result = route(app, request).value

status(result) mustBe OK

contentAsJson(result) mustBe Json.obj("address" -> testAddress, "timestamp" -> s"${testTime.toString}Z")

verifyNoInteractions(mockSubscriptionInfoService)
}
}
Expand All @@ -86,13 +85,11 @@ class VerifiedEmailControllerSpec extends SpecBase {

when(mockEmailRepository.set(any(), any())).thenReturn(Future.successful(SuccessfulEmail))

val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, getRoute)
val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, getVerifiedEmailRoute)

running(app) {
val result = route(app, request).value

status(result) mustBe OK

contentAsJson(result) mustBe Json.obj("address" -> testAddress, "timestamp" -> s"${testTime.toString}Z")
}
}
Expand All @@ -110,11 +107,86 @@ class VerifiedEmailControllerSpec extends SpecBase {

when(mockEmailRepository.set(any(), any())).thenReturn(Future.successful(FailedToRetrieveEmail))

val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, getRoute)
val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, getVerifiedEmailRoute)

running(app) {
val result = route(app, request).value
status(result) mustBe INTERNAL_SERVER_ERROR
}
}
}

"getVerifiedEmailV2" should {

"return Not Found if no data is found in the cache and SUB09 returns no email" in new Setup {
when(mockEmailRepository.get(any())).thenReturn(Future.successful(None))
when(mockSubscriptionInfoService.getSubscriberInformation(any())).thenReturn(Future.successful(None))

val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, getVerifiedEmailV2Route)

running(app) {
val result = route(app, request).value
status(result) mustBe NOT_FOUND
}
}

"return the email and not call SUB09 if the data is stored in the cache" in new Setup {
when(mockEmailRepository.get(any()))
.thenReturn(Future.successful(Some(NotificationEmail(testAddress, testTime, None))))

when(mockEmailRepository.set(any(), any())).thenReturn(Future.successful(SuccessfulEmail))
when(mockSubscriptionInfoService.getSubscriberInformation(any())).thenReturn(Future.successful(None))

val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, getVerifiedEmailV2Route)

running(app) {
val result = route(app, request).value
status(result) mustBe OK
contentAsJson(result) mustBe Json.obj("address" -> testAddress, "timestamp" -> s"${testTime.toString}Z")
verifyNoInteractions(mockSubscriptionInfoService)
}
}

"return the email and call SUB09 if the data is not stored in the cache and " +
"also store the response into the cache" in new Setup {
when(mockEmailRepository.get(any()))
.thenReturn(Future.successful(None))
.thenReturn(Future.successful(Some(NotificationEmail(testAddress, testTime, None))))

when(mockSubscriptionInfoService.getSubscriberInformation(any())).thenReturn(
Future.successful(
Some(NotificationEmail(testAddress, testTime, None))
)
)

when(mockEmailRepository.set(any(), any())).thenReturn(Future.successful(SuccessfulEmail))

val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, getVerifiedEmailV2Route)

running(app) {
val result = route(app, request).value
status(result) mustBe OK
contentAsJson(result) mustBe Json.obj("address" -> testAddress, "timestamp" -> s"${testTime.toString}Z")
}
}

"return InternalServerError if the write did not succeed when retrieving email from SUB09" in new Setup {
when(mockEmailRepository.get(any()))
.thenReturn(Future.successful(None))
.thenReturn(Future.successful(Some(NotificationEmail(testAddress, testTime, None))))

when(mockSubscriptionInfoService.getSubscriberInformation(any())).thenReturn(
Future.successful(
Some(NotificationEmail(testAddress, testTime, None))
)
)

when(mockEmailRepository.set(any(), any())).thenReturn(Future.successful(FailedToRetrieveEmail))

val request: FakeRequest[AnyContentAsEmpty.type] = FakeRequest(GET, getVerifiedEmailV2Route)

running(app) {
val result = route(app, request).value
status(result) mustBe INTERNAL_SERVER_ERROR
}
}
Expand All @@ -131,7 +203,6 @@ class VerifiedEmailControllerSpec extends SpecBase {

running(app) {
val result = route(app, request).value

status(result) mustBe INTERNAL_SERVER_ERROR
}
}
Expand All @@ -143,7 +214,6 @@ class VerifiedEmailControllerSpec extends SpecBase {

running(app) {
val result = route(app, request).value

status(result) mustBe BAD_REQUEST
}
}
Expand All @@ -157,29 +227,37 @@ class VerifiedEmailControllerSpec extends SpecBase {

running(app) {
val result = route(app, request).value

status(result) mustBe NO_CONTENT
}
}
}

trait Setup {
val testEori = "testEori"
val testEori: String = "testEori"
val testTime1: LocalDate = LocalDate.now()
val testTime: LocalDateTime = LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS)
val testAddress = "[email protected]"
val testAddress: String = "[email protected]"

val getRoute: String = routes.VerifiedEmailController.getVerifiedEmail(testEori).url
val postRoute: String = routes.VerifiedEmailController.updateVerifiedEmail().url
val enrolments: Enrolments = Enrolments(
Set(Enrolment(ENROLMENT_KEY, Seq(EnrolmentIdentifier(ENROLMENT_IDENTIFIER, testEori)), "activated"))
)

val getVerifiedEmailRoute: String = routes.VerifiedEmailController.getVerifiedEmail(testEori).url
val getVerifiedEmailV2Route: String = routes.VerifiedEmailController.getVerifiedEmailV2().url
val postRoute: String = routes.VerifiedEmailController.updateVerifiedEmail().url

val testNotificationEmail: NotificationEmail = NotificationEmail(testAddress, testTime, None)
val testTraderData: TraderData = TraderData(Seq.empty, Some(testNotificationEmail))

val mockAuthConnector: CustomAuthConnector = mock[CustomAuthConnector]
val mockEmailRepository: EmailRepository = mock[EmailRepository]
val mockSubscriptionInfoService: Sub09Connector = mock[Sub09Connector]

when(mockAuthConnector.authorise[Enrolments](any, any)(any, any)).thenReturn(Future.successful(enrolments))

def app: Application = application
.overrides(
inject.bind[CustomAuthConnector].toInstance(mockAuthConnector),
inject.bind[EmailRepository].toInstance(mockEmailRepository),
inject.bind[Sub09Connector].toInstance(mockSubscriptionInfoService)
)
Expand Down

0 comments on commit f3da262

Please sign in to comment.