Skip to content

Commit 7157e72

Browse files
Merge pull request #159 from hmrc/VOA-3635_AutoBARS_HttpClientV2-used-in-EbarsClientV2
VOA-3635 AutoBARS: `HttpClientV2` used in `EbarsClientV2`
2 parents 87549b6 + d219db2 commit 7157e72

File tree

4 files changed

+65
-81
lines changed

4 files changed

+65
-81
lines changed

app/uk/gov/hmrc/voabar/services/EbarsClientV2.scala

Lines changed: 34 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -16,82 +16,60 @@
1616

1717
package uk.gov.hmrc.voabar.services
1818

19-
import org.apache.pekko.stream.Materializer
20-
import play.api.http.Status
21-
import play.api.libs.ws.ahc.{AhcConfigBuilder, AhcWSClient, StandaloneAhcWSClient}
22-
import play.api.libs.ws.{WSAuthScheme, WSClient, WSResponse, writeableOf_urlEncodedForm}
23-
import play.api.{Configuration, Logging}
24-
import play.shaded.ahc.org.asynchttpclient.proxy.{ProxyServer, ProxyType}
25-
import play.shaded.ahc.org.asynchttpclient.{AsyncHttpClient, DefaultAsyncHttpClient, Realm}
26-
import uk.gov.hmrc.http.UnauthorizedException
19+
import play.api.Logging
20+
import play.api.http.HeaderNames.AUTHORIZATION
21+
import play.api.http.Status.{INTERNAL_SERVER_ERROR, OK, UNAUTHORIZED}
22+
import play.api.libs.ws.writeableOf_urlEncodedForm
23+
import uk.gov.hmrc.http.HttpReads.Implicits.*
24+
import uk.gov.hmrc.http.client.HttpClientV2
25+
import uk.gov.hmrc.http.{HeaderCarrier, HttpResponse, StringContextOps, UnauthorizedException}
2726
import uk.gov.hmrc.play.bootstrap.config.ServicesConfig
2827

29-
import java.util.Collections
28+
import java.net.URL
29+
import java.nio.charset.StandardCharsets.UTF_8
30+
import java.util.Base64
3031
import javax.inject.{Inject, Singleton}
3132
import scala.concurrent.duration.*
3233
import scala.concurrent.{ExecutionContext, Future}
3334
import scala.language.postfixOps
3435
import scala.util.{Failure, Success, Try}
3536
import scala.xml.XML
3637

37-
// TODO: use uk.gov.hmrc.http.client.HttpClientV2 and method .withProxy
3838
@Singleton
3939
class EbarsClientV2 @Inject() (
40-
servicesConfig: ServicesConfig,
41-
configuration: Configuration
42-
)(implicit ec: ExecutionContext,
43-
materialize: Materializer
44-
) extends Logging
45-
with Status {
40+
httpClientV2: HttpClientV2,
41+
servicesConfig: ServicesConfig
42+
)(implicit ec: ExecutionContext
43+
) extends Logging:
4644

47-
private val voaEbarsBaseUrl = servicesConfig.baseUrl("voa-ebars")
48-
private val xmlFileUploadUrl = s"$voaEbarsBaseUrl/ebars_dmz_pres_ApplicationWeb/uploadXmlSubmission"
49-
private val timeout = 120 seconds
45+
private val voaEbarsBaseUrl: String = servicesConfig.baseUrl("voa-ebars")
46+
private val xmlFileUploadURL: URL = url"$voaEbarsBaseUrl/ebars_dmz_pres_ApplicationWeb/uploadXmlSubmission"
47+
private val timeout: FiniteDuration = 120 seconds
5048

51-
private val ws: WSClient = {
52-
val proxyAhcConfig = configuration.getOptional[Boolean]("proxy.enabled") flatMap {
53-
case true => Some {
54-
val proxyHost = configuration.get[String]("proxy.host")
55-
val proxyPort = configuration.get[Int]("proxy.port")
56-
val proxyUsername = configuration.get[String]("proxy.username")
57-
val proxyPassword = configuration.get[String]("proxy.password")
58-
val realm = new Realm.Builder(proxyUsername, proxyPassword)
59-
.setScheme(Realm.AuthScheme.BASIC)
60-
.setUsePreemptiveAuth(true)
61-
.build()
62-
63-
new AhcConfigBuilder().modifyUnderlying {
64-
_.setProxyServer(new ProxyServer(proxyHost, proxyPort, proxyPort, realm, Collections.emptyList(), ProxyType.HTTP))
65-
}.build()
66-
}
67-
case _ => None
68-
}
69-
70-
val clientConfig = proxyAhcConfig.getOrElse(new AhcConfigBuilder().build())
71-
val asyncHttpClient: AsyncHttpClient = new DefaultAsyncHttpClient(clientConfig)
72-
val standaloneClient: StandaloneAhcWSClient = new StandaloneAhcWSClient(asyncHttpClient)
73-
new AhcWSClient(standaloneClient)
74-
}
49+
private def basicAuth(clientId: String, clientSecret: String): String =
50+
val encodedCredentials = Base64.getEncoder.encodeToString(s"$clientId:$clientSecret".getBytes(UTF_8))
51+
s"Basic $encodedCredentials"
7552

7653
def uploadXMl(username: String, password: String, xml: String, attempt: Int): Future[Try[Int]] =
77-
ws.url(xmlFileUploadUrl)
78-
.withAuth(username, password, WSAuthScheme.BASIC)
79-
.withRequestTimeout(timeout)
80-
.post(Map("xml" -> Seq(xml)))
54+
httpClientV2.post(xmlFileUploadURL)(using HeaderCarrier())
55+
.setHeader(AUTHORIZATION -> basicAuth(username, password))
56+
.withBody(Map("xml" -> Seq(xml)))
57+
.withProxy
58+
.transform(_.withRequestTimeout(timeout))
59+
.execute[HttpResponse]
8160
.map(processResponse(attempt))
8261

83-
private def processResponse(attempt: Int)(response: WSResponse): Try[Int] = {
62+
private def processResponse(attempt: Int)(response: HttpResponse): Try[Int] =
8463
logger.trace(s"Response : $response")
8564
response.status match {
86-
case Status.OK => parseOkResponse(response, attempt)
87-
case Status.UNAUTHORIZED => Failure(new UnauthorizedException("UNAUTHORIZED"))
88-
case status =>
65+
case OK => parseOkResponse(response, attempt)
66+
case UNAUTHORIZED => Failure(new UnauthorizedException("UNAUTHORIZED"))
67+
case status =>
8968
logger.warn(s"Couldn't send BA Reports. status: $status\n${response.body}")
90-
Failure(EbarsApiError(status, s"${response.statusText}. attempt: $attempt"))
69+
Failure(EbarsApiError(status, s"${response.status}. attempt: $attempt"))
9170
}
92-
}
9371

94-
private def parseOkResponse(response: WSResponse, attempt: Int): Try[Int] = {
72+
private def parseOkResponse(response: HttpResponse, attempt: Int): Try[Int] =
9573
val body = response.body
9674
if body.contains("401 Unauthorized") then
9775
Failure(new UnauthorizedException("UNAUTHORIZED"))
@@ -104,14 +82,11 @@ class EbarsClientV2 @Inject() (
10482
case "error" =>
10583
val errorDetail = (responseXML \ "message").text
10684
logger.warn(s"Couldn't send BA Reports. error: $errorDetail")
107-
Failure(EbarsApiError(OK, s"$errorDetail. attempt: $attempt"))
85+
Failure(EbarsApiError(INTERNAL_SERVER_ERROR, s"$errorDetail. attempt: $attempt"))
10886
}
10987
} getOrElse {
11088
logger.warn(s"Parsing eBars response failed. Body:\n$body")
111-
Failure(EbarsApiError(OK, s"Parsing eBars response failed. attempt: $attempt"))
89+
Failure(EbarsApiError(INTERNAL_SERVER_ERROR, s"Parsing eBars response failed. attempt: $attempt"))
11290
}
113-
}
114-
115-
}
11691

11792
case class EbarsApiError(status: Int, message: String) extends RuntimeException(s"$status. $message")

it/test/uk/gov/hmrc/connectors/VoaEbarsConnectorItSpec.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2024 HM Revenue & Customs
2+
* Copyright 2026 HM Revenue & Customs
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -35,25 +35,25 @@ import uk.gov.hmrc.play.bootstrap.config.ServicesConfig
3535
import uk.gov.hmrc.voabar.connectors.{DefaultVoaEbarsConnector, VoaBarAuditConnector, VoaEbarsConnector}
3636
import uk.gov.hmrc.voabar.models.EbarsRequests.BAReportRequest
3737
import uk.gov.hmrc.voabar.services.{EbarsApiError, EbarsClientV2}
38-
import com.github.tomakehurst.wiremock.client.WireMock._
38+
import com.github.tomakehurst.wiremock.client.WireMock.*
39+
import uk.gov.hmrc.http.client.HttpClientV2
3940
import uk.gov.hmrc.voabar.models.LoginDetails
4041

41-
import scala.concurrent.duration._
42+
import scala.concurrent.duration.*
4243
import scala.concurrent.{Await, ExecutionContext, Future}
4344
import scala.language.postfixOps
4445
import scala.util.{Failure, Success, Try}
4546

4647
class VoaEbarsConnectorItSpec extends PlaySpec with WiremockHelper with GuiceOneAppPerSuite with Injecting {
4748

48-
def voaEbarsConnector(port: Int) = {
49-
49+
def voaEbarsConnector(port: Int): VoaEbarsConnector = {
5050
val config = inject[Configuration]
5151

5252
val servicesConfig = new ServicesConfig(Configuration("microservice.services.voa-ebars.port" -> port).withFallback(config))
5353

5454
implicit val mat: Materializer = inject[Materializer]
5555

56-
val ebarsClientV2 = new EbarsClientV2(servicesConfig, config)
56+
val ebarsClientV2 = new EbarsClientV2(inject[HttpClientV2], servicesConfig)
5757
new DefaultVoaEbarsConnector(servicesConfig, config, ebarsClientV2, inject[VoaBarAuditConnector])
5858
}
5959

project/CodeCoverageSettings.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ object CodeCoverageSettings extends AutoPlugin {
1212
override lazy val projectSettings: Seq[Setting[?]] = Seq(
1313
ScoverageKeys.coverageExcludedPackages := excludedPackages.mkString(","),
1414
ScoverageKeys.coverageExcludedFiles := excludedFiles.mkString(","),
15-
ScoverageKeys.coverageMinimumStmtTotal := 85.0,
15+
ScoverageKeys.coverageMinimumStmtTotal := 85.2,
1616
ScoverageKeys.coverageFailOnMinimum := true,
1717
ScoverageKeys.coverageHighlighting := true
1818
)

test/uk/gov/hmrc/voabar/services/EbarsClientV2Spec.scala

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2025 HM Revenue & Customs
2+
* Copyright 2026 HM Revenue & Customs
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,28 +16,33 @@
1616

1717
package uk.gov.hmrc.voabar.services
1818

19-
import org.apache.pekko.stream.Materializer
20-
import org.apache.pekko.stream.testkit.NoMaterializer
2119
import org.scalatest.matchers.should
2220
import org.scalatest.wordspec.AnyWordSpec
2321
import play.api.Configuration
2422
import play.api.test.{DefaultAwaitTimeout, FutureAwaits}
23+
import uk.gov.hmrc.http.HeaderCarrier
24+
import uk.gov.hmrc.http.client.HttpClientV2
2525
import uk.gov.hmrc.play.bootstrap.config.ServicesConfig
26+
import org.mockito.ArgumentMatchers.any
2627

27-
import java.net.ConnectException
28-
import scala.concurrent.ExecutionContext.Implicits._
28+
import java.net.URL
29+
import scala.concurrent.ExecutionContext.Implicits.*
30+
import org.mockito.Mockito.when
31+
import org.scalatestplus.mockito.MockitoSugar
32+
import play.api.http.Status.OK
33+
import uk.gov.hmrc.voabar.connectors.RequestBuilderStub
34+
35+
import scala.util.Failure
2936

3037
/**
3138
* @author Yuriy Tumakha
3239
*/
33-
class EbarsClientV2Spec extends AnyWordSpec with should.Matchers with DefaultAwaitTimeout with FutureAwaits {
34-
35-
implicit private val mat: Materializer = NoMaterializer
40+
class EbarsClientV2Spec extends AnyWordSpec with should.Matchers with DefaultAwaitTimeout with FutureAwaits with MockitoSugar:
3641

3742
"EbarsClientV2" should {
3843
"start with only proxy and voa-ebars service configuration" in {
3944
val configuration = Configuration(
40-
"proxy.enabled" -> true,
45+
"http-verbs.proxy.enabled" -> true,
4146
"proxy.host" -> "localhost",
4247
"proxy.port" -> 9999,
4348
"proxy.username" -> "foo",
@@ -47,12 +52,16 @@ class EbarsClientV2Spec extends AnyWordSpec with should.Matchers with DefaultAwa
4752
"microservice.services.voa-ebars.protocol" -> "http"
4853
)
4954
val servicesConfig = new ServicesConfig(configuration)
50-
val ebarsClient = new EbarsClientV2(servicesConfig, configuration)
5155

52-
val thrown = intercept[ConnectException] {
53-
await(ebarsClient.uploadXMl("", "", "<xml/>", 1))
54-
}
55-
thrown.getMessage shouldBe "Connection refused: localhost/127.0.0.1:9999"
56+
val httpClientV2Mock = mock[HttpClientV2]
57+
when(
58+
httpClientV2Mock.post(any[URL])(using any[HeaderCarrier])
59+
).thenReturn(RequestBuilderStub(Right(OK), ""))
60+
61+
val ebarsClient = new EbarsClientV2(httpClientV2Mock, servicesConfig)
62+
63+
await(ebarsClient.uploadXMl("user", "pass", "<xml/>", 1)) shouldBe Failure(
64+
EbarsApiError(500, "Parsing eBars response failed. attempt: 1")
65+
)
5666
}
5767
}
58-
}

0 commit comments

Comments
 (0)