Skip to content

Commit

Permalink
Merge pull request #267 from hmrc/ITSASU-3081
Browse files Browse the repository at this point in the history
ITSASU-3081 - Update sign up API to handle and return an alternate re…
  • Loading branch information
srikanthsunkara79 authored May 13, 2024
2 parents 6bcbf3d + e65d3f7 commit 6229ac9
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 28 deletions.
6 changes: 5 additions & 1 deletion app/controllers/SignUpController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package controllers
import common.Extractors
import config.AppConfig
import connectors.SignUpTaxYearConnector
import models.SignUpResponse.{AlreadySignedUp, SignUpSuccess}
import models.monitoring.{RegistrationFailureAudit, RegistrationSuccessAudit}
import play.api.libs.json.Json
import play.api.mvc._
Expand Down Expand Up @@ -53,12 +54,15 @@ class SignUpController @Inject()(authService: AuthService,

private def signUp(nino: String, taxYear: String, agentReferenceNumber: Option[String])(implicit request: Request[AnyContent]) =
signUpTaxYearConnector.signUp(nino, taxYear).map {
case Right(response) =>
case Right(response: SignUpSuccess) =>
val path: Option[String] = request.headers.get(ITSASessionKeys.RequestURI)
auditService.audit(RegistrationSuccessAudit(
agentReferenceNumber, nino, response.mtdbsa, appConfig.signUpServiceAuthorisationToken, path
))
Ok(Json.toJson(response))
case Right(AlreadySignedUp) =>
logger.warn(s"[SignUpController][signUp] - sign up returned customer already signed up")
UnprocessableEntity("Customer already signed up")
case Left(error) =>
logger.error(s"Error processing Sign up request with status ${error.status} and message ${error.reason}")
auditService.audit(RegistrationFailureAudit(nino, error.status, error.reason))
Expand Down
10 changes: 8 additions & 2 deletions app/models/SignUpResponse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,14 @@ package models

import play.api.libs.json.{Json, OFormat}

case class SignUpResponse(mtdbsa: String)
sealed trait SignUpResponse

object SignUpResponse {
implicit val format: OFormat[SignUpResponse] = Json.format[SignUpResponse]

case class SignUpSuccess(mtdbsa: String) extends SignUpResponse

case object AlreadySignedUp extends SignUpResponse

implicit val format: OFormat[SignUpSuccess] = Json.format[SignUpSuccess]

}
18 changes: 14 additions & 4 deletions app/parsers/SignUpParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,33 @@

package parsers

import models.SignUpResponse.SignUpSuccess
import models.{ErrorModel, SignUpResponse}
import play.api.http.Status.OK
import play.api.http.Status.{OK, UNPROCESSABLE_ENTITY}
import play.api.libs.json.{JsError, JsSuccess}
import uk.gov.hmrc.http.{HttpReads, HttpResponse}


object SignUpParser {

type PostSignUpResponse = Either[ErrorModel, SignUpResponse]

implicit val signUpResponseHttpReads: HttpReads[PostSignUpResponse] = {
(_: String, _: String, response: HttpResponse) =>
response.status match {
case OK => response.json.asOpt[SignUpResponse] match {
case Some(successResponse) => Right(successResponse)
case None => Left(ErrorModel(OK, "Failed to read Json for MTD Sign Up Response"))
case OK => response.json.validate[SignUpSuccess] match {
case JsSuccess(value, _) => Right(value)
case JsError(_) => Left(ErrorModel(OK, s"Failed to read Json for MTD Sign Up Response"))
}
case UNPROCESSABLE_ENTITY => (response.json \ "failures" \\ "code").map(_.validate[String]).headOption match {
case Some(JsSuccess(CustomerAlreadySignedUp, _)) => Right(SignUpResponse.AlreadySignedUp)
case Some(JsSuccess(code, _)) => Left(ErrorModel(UNPROCESSABLE_ENTITY, code))
case _ => Left(ErrorModel(UNPROCESSABLE_ENTITY, s"Failed to read Json for MTD Sign Up Response"))
}
case status => Left(ErrorModel(status, response.body))
}
}

private val CustomerAlreadySignedUp: String = "CUSTOMER_ALREADY_SIGNED_UP"

}
79 changes: 71 additions & 8 deletions it/test/connectors/SignUpTaxYearConnectorISpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ import config.MicroserviceAppConfig
import helpers.ComponentSpecBase
import helpers.IntegrationTestConstants._
import helpers.servicemocks.SignUpTaxYearStub
import models.{ErrorModel, SignUpResponse}
import models.ErrorModel
import models.SignUpResponse.{AlreadySignedUp, SignUpSuccess}
import play.api.http.Status._
import play.api.libs.json.Json
import play.api.mvc.Request
import play.api.test.FakeRequest

Expand All @@ -35,35 +37,96 @@ class SignUpTaxYearConnectorISpec extends ComponentSpecBase {
"The sign up tax year connector" when {
"receiving a 200 response" should {
"return a valid MTDBSA number when valid json is found" in {
SignUpTaxYearStub.stubSignUp(testTaxYearSignUpSubmission(testNino, testTaxYear), appConfig.signUpServiceAuthorisationToken, appConfig.signUpServiceEnvironment)(
SignUpTaxYearStub.stubSignUp(
testTaxYearSignUpSubmission(testNino, testTaxYear),
appConfig.signUpServiceAuthorisationToken,
appConfig.signUpServiceEnvironment
)(
OK, testSignUpSuccessBody
)

val result = signUpConnector.signUp(testNino, testTaxYear)

result.futureValue shouldBe Right(SignUpResponse("XQIT00000000001"))
result.futureValue shouldBe Right(SignUpSuccess("XQIT00000000001"))
}

"return a Json parse failure when invalid json is found" in {
SignUpTaxYearStub.stubSignUp(testTaxYearSignUpSubmission(testNino, testTaxYear), appConfig.signUpServiceAuthorisationToken, appConfig.signUpServiceEnvironment)(
SignUpTaxYearStub.stubSignUp(
testTaxYearSignUpSubmission(testNino, testTaxYear),
appConfig.signUpServiceAuthorisationToken,
appConfig.signUpServiceEnvironment
)(
OK, testSignUpInvalidBody
)

val result = signUpConnector.signUp(testNino, testTaxYear)

result.futureValue shouldBe Left(ErrorModel(status = 200, "Failed to read Json for MTD Sign Up Response"))
result.futureValue shouldBe Left(ErrorModel(status = OK, "Failed to read Json for MTD Sign Up Response"))
}
}

"receiving a non-200 response" should {
"receiving a 422 response with a customer already signed up code" should {
"return a already signed up result" in {
SignUpTaxYearStub.stubSignUp(
testTaxYearSignUpSubmission(testNino, testTaxYear),
appConfig.signUpServiceAuthorisationToken,
appConfig.signUpServiceEnvironment
)(
status = UNPROCESSABLE_ENTITY,
body = Json.obj("failures" -> Json.arr(
Json.obj("code" -> "CUSTOMER_ALREADY_SIGNED_UP", "reason" -> "The customer is already signed up")
))
)

val result = signUpConnector.signUp(testNino, testTaxYear)

result.futureValue shouldBe Right(AlreadySignedUp)
}

"return a Json parse failure when invalid json is found" in {
SignUpTaxYearStub.stubSignUp(
testTaxYearSignUpSubmission(testNino, testTaxYear),
appConfig.signUpServiceAuthorisationToken,
appConfig.signUpServiceEnvironment
)(
UNPROCESSABLE_ENTITY, testSignUpInvalidBody
)

val result = signUpConnector.signUp(testNino, testTaxYear)

result.futureValue shouldBe Left(ErrorModel(status = UNPROCESSABLE_ENTITY, "Failed to read Json for MTD Sign Up Response"))
}
}

"receiving a 422 response without customer already signed up code" should {
"return the status and error received" in {
SignUpTaxYearStub.stubSignUp(
testTaxYearSignUpSubmission(testNino, testTaxYear),
appConfig.signUpServiceAuthorisationToken,
appConfig.signUpServiceEnvironment
)(
UNPROCESSABLE_ENTITY, failureResponse("code", "reason")
)

val result = signUpConnector.signUp(testNino, testTaxYear)

result.futureValue shouldBe Left(ErrorModel(UNPROCESSABLE_ENTITY, "code"))
}
}

"receiving a 500 response" should {
"return the status and error received" in {
SignUpTaxYearStub.stubSignUp(testTaxYearSignUpSubmission(testNino, testTaxYear), appConfig.signUpServiceAuthorisationToken, appConfig.signUpServiceEnvironment)(
SignUpTaxYearStub.stubSignUp(
testTaxYearSignUpSubmission(testNino, testTaxYear),
appConfig.signUpServiceAuthorisationToken,
appConfig.signUpServiceEnvironment
)(
INTERNAL_SERVER_ERROR, failureResponse("code", "reason")
)

val result = signUpConnector.signUp(testNino, testTaxYear)

result.futureValue shouldBe Left(ErrorModel(INTERNAL_SERVER_ERROR, """{"code":"code","reason":"reason"}"""))
result.futureValue shouldBe Left(ErrorModel(INTERNAL_SERVER_ERROR, """{"failures":[{"code":"code","reason":"reason"}]}"""))
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions it/test/controllers/SignUpControllerISpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import config.featureswitch.FeatureSwitching
import helpers.ComponentSpecBase
import helpers.IntegrationTestConstants._
import helpers.servicemocks.{AuthStub, SignUpTaxYearStub}
import models.SignUpResponse
import models.SignUpResponse.SignUpSuccess
import play.api.Configuration
import play.api.http.Status._

Expand All @@ -47,7 +47,7 @@ class SignUpControllerISpec extends ComponentSpecBase with FeatureSwitching {
httpStatus(OK)
)
res should have(
jsonBodyAs[SignUpResponse](SignUpResponse("XQIT00000000001"))
jsonBodyAs[SignUpSuccess](SignUpSuccess("XQIT00000000001"))
)
}

Expand Down
8 changes: 6 additions & 2 deletions it/test/helpers/IntegrationTestConstants.scala
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,12 @@ object IntegrationTestConstants extends JsonUtils {
def failureResponse(code: String, reason: String): JsValue =
s"""
|{
| "code":"$code",
| "reason":"$reason"
| "failures":[
| {
| "code":"$code",
| "reason":"$reason"
| }
| ]
|}
""".stripMargin

Expand Down
20 changes: 17 additions & 3 deletions test/controllers/SignUpControllerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ package controllers
import common.CommonSpec
import config.MicroserviceAppConfig
import config.featureswitch.FeatureSwitching
import models.SignUpResponse.SignUpSuccess
import models.monitoring.{RegistrationFailureAudit, RegistrationSuccessAudit}
import models.{ErrorModel, SignUpResponse}
import play.api.Configuration
import play.api.http.Status.{INTERNAL_SERVER_ERROR, OK}
import play.api.http.Status.{INTERNAL_SERVER_ERROR, OK, UNPROCESSABLE_ENTITY}
import play.api.libs.json.Json
import play.api.mvc.{ControllerComponents, Result}
import play.api.test.FakeRequest
Expand Down Expand Up @@ -64,7 +65,7 @@ class SignUpControllerSpec extends CommonSpec
val fakeRequest = FakeRequest().withJsonBody(Json.toJson(testTaxYearSignUpSubmission(testNino, testTaxYear)))

mockRetrievalSuccess(Enrolments(Set()))
signUpTaxYear(testNino, testTaxYear)(Future.successful(Right(SignUpResponse("XAIT000000"))))
signUpTaxYear(testNino, testTaxYear)(Future.successful(Right(SignUpSuccess("XAIT000000"))))

val result: Future[Result] = TestController.signUp(testNino, testTaxYear)(fakeRequest)

Expand All @@ -78,7 +79,7 @@ class SignUpControllerSpec extends CommonSpec
val fakeRequest = FakeRequest().withJsonBody(Json.toJson(testTaxYearSignUpSubmission(testNino, testTaxYear)))

mockRetrievalSuccess(Enrolments(Set(Enrolment(hmrcAsAgent, Seq(EnrolmentIdentifier("AgentReferenceNumber", "123456789")), "Activated"))))
signUpTaxYear(testNino, testTaxYear)(Future.successful(Right(SignUpResponse("XAIT000000"))))
signUpTaxYear(testNino, testTaxYear)(Future.successful(Right(SignUpSuccess("XAIT000000"))))

val result: Future[Result] = TestController.signUp(testNino, testTaxYear)(fakeRequest)

Expand All @@ -90,6 +91,19 @@ class SignUpControllerSpec extends CommonSpec
}
}
}
"return UnprocessableEntity" when {
"the customer is already signed up" in {
val fakeRequest = FakeRequest()

mockRetrievalSuccess(Enrolments(Set()))
signUpTaxYear(testNino, testTaxYear)(Future.successful(Right(SignUpResponse.AlreadySignedUp)))

val result: Future[Result] = TestController.signUp(testNino, testTaxYear)(fakeRequest)

status(result) shouldBe UNPROCESSABLE_ENTITY
contentAsString(result) shouldBe "Customer already signed up"
}
}
"return InternalServerError" when {
"sign up was unsuccessful and an error was returned" in {
val fakeRequest = FakeRequest().withJsonBody(Json.toJson(testTaxYearSignUpSubmission(testNino, testTaxYear)))
Expand Down
43 changes: 37 additions & 6 deletions test/parsers/SignUpParserSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
package parsers

import common.CommonSpec
import models.{ErrorModel, SignUpResponse}
import models.ErrorModel
import models.SignUpResponse.{AlreadySignedUp, SignUpSuccess}
import play.api.http.Status._
import play.api.libs.json.Json
import uk.gov.hmrc.http.HttpResponse
Expand All @@ -28,10 +29,10 @@ class SignUpParserSpec extends CommonSpec {

"supplied with an OK response" should {

"return a SignUpResponse when the response has valid json" in {
val response = HttpResponse(OK, body = Json.toJson(SignUpResponse("XAIT000000")).toString())
"return a SignUpSuccess when the response has valid json" in {
val response = HttpResponse(OK, body = Json.toJson(SignUpSuccess("XAIT000000")).toString())

SignUpParser.signUpResponseHttpReads.read("", "", response) shouldBe Right(SignUpResponse("XAIT000000"))
SignUpParser.signUpResponseHttpReads.read("", "", response) shouldBe Right(SignUpSuccess("XAIT000000"))
}

"return a SignUpFailure when the response has invalid json" in {
Expand All @@ -46,12 +47,42 @@ class SignUpParserSpec extends CommonSpec {
}
}

"supplied with a non-OK response" should {
"supplied with a Unprocessable entity response" should {
"return a already signed up when the response has a customer already signed up code" in {
val response = HttpResponse(
status = UNPROCESSABLE_ENTITY,
body = Json.obj("failures" -> Json.arr(
Json.obj("code" -> "CUSTOMER_ALREADY_SIGNED_UP")
)).toString()
)

SignUpParser.signUpResponseHttpReads.read("", "", response) shouldBe Right(AlreadySignedUp)
}

"return a sign up failure when the response hs not got a customer already signed up code" in {
val response = HttpResponse(
status = UNPROCESSABLE_ENTITY,
body = Json.obj("failures" -> Json.arr(Json.obj("code" -> "OTHER_CODE"))).toString()
)

SignUpParser.signUpResponseHttpReads.read("", "", response) shouldBe
Left(ErrorModel(UNPROCESSABLE_ENTITY, "OTHER_CODE"))
}

"return a sign up failure when the response json has no code" in {
val response = HttpResponse(UNPROCESSABLE_ENTITY, body = Json.obj().toString())

SignUpParser.signUpResponseHttpReads.read("", "", response) shouldBe
Left(ErrorModel(UNPROCESSABLE_ENTITY, s"Failed to read Json for MTD Sign Up Response"))
}
}

"supplied with a non OK or Unprocessable entity response" should {

"return a SignUpFailure with the response message" in {
val response = HttpResponse(INTERNAL_SERVER_ERROR, body = "Error body")

SignUpParser.signUpResponseHttpReads.read("", "", response) shouldBe Left(ErrorModel(INTERNAL_SERVER_ERROR, "Error body"))
SignUpParser.signUpResponseHttpReads.read("", "", response) shouldBe Left(ErrorModel(INTERNAL_SERVER_ERROR, "Error body"))
}
}
}
Expand Down

0 comments on commit 6229ac9

Please sign in to comment.