Skip to content

Commit

Permalink
Merge pull request #239 from hmrc/ITSASU-1446
Browse files Browse the repository at this point in the history
ITSASU-1446: Create status-determination-service Connector
  • Loading branch information
aug24 authored Aug 2, 2022
2 parents 2c7017d + 428c527 commit 502617a
Show file tree
Hide file tree
Showing 9 changed files with 327 additions and 1 deletion.
3 changes: 3 additions & 0 deletions app/config/MicroserviceAppConfig.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ trait AppConfig {
val desAuthorisationToken: String
val desEnvironmentHeader: (String, String)

def statusDeterminationServiceURL: String
}

@Singleton
Expand All @@ -55,6 +56,8 @@ class MicroserviceAppConfig @Inject()(servicesConfig: ServicesConfig, val config
override lazy val ggURL: String = servicesConfig.baseUrl("government-gateway")
override lazy val ggAdminURL: String = servicesConfig.baseUrl("gg-admin")

override lazy val statusDeterminationServiceURL = servicesConfig.baseUrl("status-determination-service")

private def desBase =
if (FeatureSwitching.isEnabled(featureswitch.StubDESFeature, configuration)) "microservice.services.stub-des"
else "microservice.services.des"
Expand Down
41 changes: 41 additions & 0 deletions app/connectors/MandationStatusConnector.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2022 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 connectors

import config.AppConfig
import models.status.MandationStatusRequest
import parsers.MandationStatusParser.{PostMandationStatusResponse, mandationStatusResponseHttpReads}
import uk.gov.hmrc.http.{HeaderCarrier, HttpClient}

import javax.inject.{Inject, Singleton}
import scala.concurrent.{ExecutionContext, Future}

@Singleton
class MandationStatusConnector @Inject()(httpClient: HttpClient,
appConfig: AppConfig)(implicit ec: ExecutionContext) {
def getMandationStatus(nino: String, utr: String)
(implicit hc: HeaderCarrier): Future[PostMandationStatusResponse] = {
val requestBody = MandationStatusRequest(nino, utr)

httpClient.POST[MandationStatusRequest, PostMandationStatusResponse](
url = getMandationStatusUrl,
body = requestBody
)
}

private val getMandationStatusUrl = s"${appConfig.statusDeterminationServiceURL}/itsa-status"
}
39 changes: 39 additions & 0 deletions app/parsers/MandationStatusParser.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2022 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 parsers

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

object MandationStatusParser {
type PostMandationStatusResponse = Either[ErrorModel, MandationStatusResponse]

implicit val mandationStatusResponseHttpReads: HttpReads[PostMandationStatusResponse] =
(_: String, _: String, response: HttpResponse) => {
response.status match {
case OK => response.json.validate[MandationStatusResponse] match {
case JsSuccess(value, _) => Right(value)
case JsError(errors) =>
Left(ErrorModel(OK, s"Invalid Json for mandationStatusResponseHttpReads: $errors"))
}
case status => Left(ErrorModel(status, response.body))
}
}
}
5 changes: 5 additions & 0 deletions conf/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@ microservice {
port = 9602
}

status-determination-service {
host = localhost
port = 9562
}

des {
url = "http://localhost:9562"
environment = "dev"
Expand Down
71 changes: 71 additions & 0 deletions it/connectors/MandationStatusConnectorISpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2020 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 connectors

import helpers.ComponentSpecBase
import helpers.IntegrationTestConstants.failureResponse
import helpers.servicemocks.GetMandationStatusStub
import models.ErrorModel
import models.status.MandationStatus.Voluntary
import models.status.{MandationStatusRequest, MandationStatusResponse}
import play.api.http.Status.{INTERNAL_SERVER_ERROR, OK}
import play.api.libs.json.Json
import play.api.mvc.Request
import play.api.test.FakeRequest

class MandationStatusConnectorISpec extends ComponentSpecBase {
private lazy val connector: MandationStatusConnector = app.injector.instanceOf[MandationStatusConnector]
implicit val request: Request[_] = FakeRequest()

"MandationStatusConnector" should {
"return MandationStatusResponse" when {
"the status determination service returns OK and a valid JSON response body" in {
GetMandationStatusStub.stub(
Json.toJson(MandationStatusRequest("test-nino", "test-utr"))
)(OK, Json.toJson(MandationStatusResponse(currentYearStatus = Voluntary, nextYearStatus = Voluntary)))

val result = connector.getMandationStatus("test-nino", "test-utr")

result.futureValue shouldBe Right(MandationStatusResponse(currentYearStatus = Voluntary, nextYearStatus = Voluntary))
}
}

"return an exception" when {
"the status determination service returns OK and an invalid JSON response body" in {
GetMandationStatusStub.stubInvalidResponse(
Json.toJson(MandationStatusRequest("test-nino", "test-utr"))
)(OK, "{ currentYearStatus")

val result = connector.getMandationStatus("test-nino", "test-utr")

result.futureValue shouldBe Left(ErrorModel(OK, "Invalid Json for mandationStatusResponseHttpReads: List((,List(JsonValidationError(List(error.expected.jsobject),WrappedArray()))))"))
}
}

"return the status and error received" when {
"the status determination service returns a failure" in {
GetMandationStatusStub.stub(
Json.toJson(MandationStatusRequest("test-nino", "test-utr"))
)(INTERNAL_SERVER_ERROR, failureResponse("code", "reason"))

val result = connector.getMandationStatus("test-nino", "test-utr")

result.futureValue shouldBe Left(ErrorModel(INTERNAL_SERVER_ERROR, """{"code":"code","reason":"reason"}"""))
}
}
}
}
4 changes: 3 additions & 1 deletion it/helpers/ComponentSpecBase.scala
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ trait ComponentSpecBase extends AnyWordSpecLike
"microservice.services.throttle-threshold" -> "2",
"throttle.testThrottle.max"-> "10",
"throttle.zeroTestThrottle.max"-> "0",
"throttle.oneTestThrottle.max" -> "1"
"throttle.oneTestThrottle.max" -> "1",
"microservice.services.status-determination-service.host" -> mockHost,
"microservice.services.status-determination-service.port" -> mockPort,
) ++ overriddenConfig()

def overriddenConfig(): Map[String, String] = Map.empty
Expand Down
38 changes: 38 additions & 0 deletions it/helpers/servicemocks/GetMandationStatusStub.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2020 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 helpers.servicemocks

import com.github.tomakehurst.wiremock.stubbing.StubMapping
import play.api.libs.json.JsValue

object GetMandationStatusStub extends WireMockMethods {
def stub(expectedBody: JsValue)(status: Int, body: JsValue): StubMapping = {
when(
method = POST,
uri = "/itsa-status",
body = expectedBody
).thenReturn(status, body)
}

def stubInvalidResponse(expectedBody: JsValue)(status: Int, body: String): StubMapping = {
when(
method = POST,
uri = "/itsa-status",
body = expectedBody
).thenReturn(status, body)
}
}
59 changes: 59 additions & 0 deletions test/connectors/MandationStatusConnectorSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2022 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 connectors

import common.CommonSpec
import config.AppConfig
import connectors.mocks.MockHttp
import models.status.MandationStatus.Voluntary
import models.status.{MandationStatusRequest, MandationStatusResponse}
import org.mockito.ArgumentMatchers
import org.mockito.Mockito.when
import org.scalatestplus.play.guice.GuiceOneAppPerSuite
import parsers.MandationStatusParser.PostMandationStatusResponse
import play.api.mvc.Request
import play.api.test.FakeRequest
import play.api.test.Helpers.{await, defaultAwaitTimeout}
import uk.gov.hmrc.http.HeaderCarrier

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

class MandationStatusConnectorSpec extends CommonSpec with MockHttp with GuiceOneAppPerSuite {
implicit val hc = HeaderCarrier()
implicit val request: Request[_] = FakeRequest()

val appConfig: AppConfig = app.injector.instanceOf[AppConfig]
val connector = new MandationStatusConnector(mockHttpClient, appConfig)

val headers = Seq()

"MandationStatusConnector" should {
"retrieve the user mandation status" in {
when(mockHttpClient.POST[MandationStatusRequest, PostMandationStatusResponse](
ArgumentMatchers.eq(s"${appConfig.statusDeterminationServiceURL}/itsa-status"),
ArgumentMatchers.eq(MandationStatusRequest("test-nino", "test-utr")),
ArgumentMatchers.any()
)(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any()))
.thenReturn(Future.successful(Right(MandationStatusResponse(currentYearStatus = Voluntary, nextYearStatus = Voluntary))))

await(
connector.getMandationStatus("test-nino", "test-utr")
) shouldBe Right(MandationStatusResponse(currentYearStatus = Voluntary, nextYearStatus = Voluntary))
}
}
}
68 changes: 68 additions & 0 deletions test/parsers/MandationStatusParserSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright 2022 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 parsers

import common.CommonSpec
import models.ErrorModel
import models.status.MandationStatus.Voluntary
import models.status.MandationStatusResponse
import play.api.http.Status.{INTERNAL_SERVER_ERROR, OK}
import play.api.libs.json.Json
import uk.gov.hmrc.http.HttpResponse

class MandationStatusParserSpec extends CommonSpec {
"MandationStatusParser" should {
"return MandationStatusResponse" when {
"supplied with an OK response and valid JSON" in {
val response = HttpResponse(
OK,
body = Json.toJson(MandationStatusResponse(currentYearStatus = Voluntary, nextYearStatus = Voluntary)).toString()
)

MandationStatusParser.mandationStatusResponseHttpReads.read("POST", "test-url", response) shouldBe
Right(MandationStatusResponse(currentYearStatus = Voluntary, nextYearStatus = Voluntary))
}
}

"return an error" when {
"supplied with an OK response and invalid JSON" in {
val response = HttpResponse(OK, body =
"""
|{
| "invalid" : "json"
|}
""".stripMargin)

val expectedError = "Invalid Json for mandationStatusResponseHttpReads: " +
"List(" +
"(/currentYearStatus,List(JsonValidationError(List(error.path.missing),WrappedArray()))), " +
"(/nextYearStatus,List(JsonValidationError(List(error.path.missing),WrappedArray())))" +
")"

MandationStatusParser.mandationStatusResponseHttpReads.read("POST", "test-url", response) shouldBe
Left(ErrorModel(OK, expectedError))
}

"supplied with a failed response" in {
val response = HttpResponse(INTERNAL_SERVER_ERROR, body = "Error body")

MandationStatusParser.mandationStatusResponseHttpReads.read("POST", "test-url", response) shouldBe
Left(ErrorModel(INTERNAL_SERVER_ERROR, "Error body"))
}
}
}
}

0 comments on commit 502617a

Please sign in to comment.