Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API-7737: Upgrade to Java 21 with HttpClientV2 #111

Merged
merged 5 commits into from
Aug 1, 2024
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
4 changes: 2 additions & 2 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version="3.5.3"
version="3.7.17"
runner.dialect = "scala213"

maxColumn = 180
Expand Down Expand Up @@ -36,4 +36,4 @@ rewrite.rules = [AsciiSortImports]
verticalMultiline.atDefnSite = true
verticalMultiline.newlineAfterOpenParen = true

includeNoParensInSelectChains = true
includeNoParensInSelectChains = true
3 changes: 0 additions & 3 deletions app/Module.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ import com.google.inject.AbstractModule
import io.swagger.v3.parser.OpenAPIV3Parser
import io.swagger.v3.parser.core.extensions.SwaggerParserExtension

import uk.gov.hmrc.http.HttpClient
import uk.gov.hmrc.play.bootstrap.http.DefaultHttpClient
import uk.gov.hmrc.ramltools.loaders.{RamlLoader, UrlRewriter}

import uk.gov.hmrc.apipublisher.connectors.OASFileLoader.{MicroserviceOASFileLocator, OASFileLocator}
Expand All @@ -31,7 +29,6 @@ class Module extends AbstractModule {
override def configure(): Unit = {
bind(classOf[UrlRewriter]).to(classOf[DocumentationUrlRewriter])
bind(classOf[RamlLoader]).to(classOf[DocumentationRamlLoader])
bind(classOf[HttpClient]).to(classOf[DefaultHttpClient])
bind(classOf[SwaggerParserExtension]).toInstance(new OpenAPIV3Parser)
bind(classOf[OASFileLocator]).toInstance(MicroserviceOASFileLocator)
bind(classOf[OasVersionDefinitionService.OasParser]).toInstance(new OasParserImpl)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,27 +22,34 @@ import scala.concurrent.{ExecutionContext, Future}
import play.api.http.Status.{BAD_REQUEST, UNPROCESSABLE_ENTITY}
import play.api.libs.json.{JsObject, JsString, JsValue, Json}
import uk.gov.hmrc.http.HttpReads.Implicits._
import uk.gov.hmrc.http.{HeaderCarrier, HttpClient, HttpResponse, UnprocessableEntityException, UpstreamErrorResponse}
import uk.gov.hmrc.http.client.HttpClientV2
import uk.gov.hmrc.http.{HeaderCarrier, HttpResponse, StringContextOps, UnprocessableEntityException, UpstreamErrorResponse}

import uk.gov.hmrc.apipublisher.util.ApplicationLogger

@Singleton
class APIDefinitionConnector @Inject() (config: ApiDefinitionConfig, http: HttpClient)(implicit val ec: ExecutionContext)
class APIDefinitionConnector @Inject() (config: ApiDefinitionConfig, http: HttpClientV2)(implicit val ec: ExecutionContext)
extends ConnectorRecovery with ApplicationLogger {

lazy val serviceBaseUrl = config.baseUrl

def publishAPI(api: JsObject)(implicit hc: HeaderCarrier): Future[Unit] = {
http.POST[JsObject, Either[UpstreamErrorResponse, HttpResponse]](s"$serviceBaseUrl/api-definition", api, Seq.empty).map {
case Right(_) => (())
case Left(UpstreamErrorResponse(message, UNPROCESSABLE_ENTITY, _, _)) => throw new UnprocessableEntityException(message)
case Left(err) => throw err
}
http.post(url"$serviceBaseUrl/api-definition")
.withBody(Json.toJson(api))
.execute[Either[UpstreamErrorResponse, HttpResponse]]
.map {
case Right(_) => (())
case Left(UpstreamErrorResponse(message, UNPROCESSABLE_ENTITY, _, _)) => throw new UnprocessableEntityException(message)
case Left(err) => throw err
}
}

def validateAPIDefinition(definition: JsObject)(implicit hc: HeaderCarrier): Future[Option[JsValue]] = {
val url = s"$serviceBaseUrl/api-definition/validate"
http.POST[JsObject, Either[UpstreamErrorResponse, HttpResponse]](url, definition ++ Json.obj("serviceBaseUrl" -> "dummy", "serviceName" -> "dummy"))
val url = url"$serviceBaseUrl/api-definition/validate"
http
.post(url)
.withBody(definition ++ Json.obj("serviceBaseUrl" -> "dummy", "serviceName" -> "dummy"))
.execute[Either[UpstreamErrorResponse, HttpResponse]]
.map {
case Right(_) => None
case Left(UpstreamErrorResponse(message, BAD_REQUEST, _, _)) =>
Expand Down
28 changes: 17 additions & 11 deletions app/uk/gov/hmrc/apipublisher/connectors/APIScopeConnector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,37 @@ import javax.inject.{Inject, Singleton}
import scala.concurrent.{ExecutionContext, Future}

import play.api.http.Status.{BAD_REQUEST, UNPROCESSABLE_ENTITY}
import play.api.libs.json.{JsString, JsValue}
import play.api.libs.json.{JsString, JsValue, Json}
import uk.gov.hmrc.http.HttpReads.Implicits._
import uk.gov.hmrc.http._
import uk.gov.hmrc.http.client.HttpClientV2

import uk.gov.hmrc.apipublisher.models.Scope
import uk.gov.hmrc.apipublisher.util.ApplicationLogger

@Singleton
class APIScopeConnector @Inject() (config: ApiScopeConfig, http: HttpClient)(implicit val ec: ExecutionContext)
class APIScopeConnector @Inject() (config: ApiScopeConfig, http: HttpClientV2)(implicit val ec: ExecutionContext)
extends ConnectorRecovery with ApplicationLogger {

lazy val serviceBaseUrl = config.baseUrl

def publishScopes(scopes: JsValue)(implicit hc: HeaderCarrier): Future[Unit] = {
http.POST[JsValue, Either[UpstreamErrorResponse, HttpResponse]](s"$serviceBaseUrl/scope", scopes).map {
case Right(_) => (())
case Left(UpstreamErrorResponse(message, UNPROCESSABLE_ENTITY, _, _)) => throw new UnprocessableEntityException(message)
case Left(err) => throw err
}
http
.post(url"$serviceBaseUrl/scope")
.withBody(scopes)
.execute[Either[UpstreamErrorResponse, HttpResponse]]
.map {
case Right(_) => (())
case Left(UpstreamErrorResponse(message, UNPROCESSABLE_ENTITY, _, _)) => throw new UnprocessableEntityException(message)
case Left(err) => throw err
}
}

def validateScopes(scopes: JsValue)(implicit hc: HeaderCarrier): Future[Option[JsValue]] = {
val url = s"$serviceBaseUrl/scope/validate"
http.POST[JsValue, Either[UpstreamErrorResponse, HttpResponse]](url, scopes)
val url = url"$serviceBaseUrl/scope/validate"
http.post(url)
.withBody(Json.toJson(scopes))
.execute[Either[UpstreamErrorResponse, HttpResponse]]
.map {
case Right(_) => None
case Left(UpstreamErrorResponse(message, BAD_REQUEST, _, _)) =>
Expand All @@ -54,8 +61,7 @@ class APIScopeConnector @Inject() (config: ApiScopeConfig, http: HttpClient)(imp
}

def retrieveScopes(scopeKeys: Set[String])(implicit hc: HeaderCarrier): Future[Seq[Scope]] = {
val url = url"$serviceBaseUrl/scope?keys=${scopeKeys.mkString(" ")}"
http.GET[Seq[Scope]](url)
http.get(url"$serviceBaseUrl/scope?keys=${scopeKeys.mkString(" ")}").execute[Seq[Scope]]
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,33 @@

package uk.gov.hmrc.apipublisher.connectors

import java.net.URLEncoder
import java.nio.charset.StandardCharsets
import javax.inject.{Inject, Singleton}
import scala.concurrent.{ExecutionContext, Future}

import play.api.http.Status.{BAD_REQUEST, UNPROCESSABLE_ENTITY}
import play.api.libs.json.{JsString, JsValue}
import play.api.libs.json.{JsString, JsValue, Json}
import uk.gov.hmrc.http.HttpReads.Implicits._
import uk.gov.hmrc.http.{HeaderCarrier, HttpClient, HttpResponse, UnprocessableEntityException, UpstreamErrorResponse}
import uk.gov.hmrc.http.client.HttpClientV2
import uk.gov.hmrc.http.{HeaderCarrier, HttpResponse, StringContextOps, UnprocessableEntityException, UpstreamErrorResponse}

import uk.gov.hmrc.apipublisher.models.{ApiFieldDefinitions, ApiSubscriptionFieldDefinitionsRequest, FieldDefinition}

@Singleton
class APISubscriptionFieldsConnector @Inject() (config: ApiSSubscriptionFieldsConfig, http: HttpClient)(implicit val ec: ExecutionContext)
class APISubscriptionFieldsConnector @Inject() (config: ApiSSubscriptionFieldsConfig, http: HttpClientV2)(implicit val ec: ExecutionContext)
extends ConnectorRecovery {

lazy val serviceBaseUrl = config.baseUrl

def publishFieldDefinitions(apiFieldDefinitions: Seq[ApiFieldDefinitions])(implicit hc: HeaderCarrier): Future[Unit] = {
val putFutures: Iterable[Future[Unit]] = apiFieldDefinitions.map {
case ApiFieldDefinitions(apiContext, apiVersion, fieldDefinitions) =>
val urlEncodedApiContext = URLEncoder.encode(apiContext, StandardCharsets.UTF_8.name)
val request = ApiSubscriptionFieldDefinitionsRequest(fieldDefinitions)
val putUrl = s"$serviceBaseUrl/definition/context/$urlEncodedApiContext/version/$apiVersion"
http.PUT[ApiSubscriptionFieldDefinitionsRequest, Either[UpstreamErrorResponse, HttpResponse]](putUrl, request)
http.put(url"$serviceBaseUrl/definition/context/$apiContext/version/$apiVersion")
.withBody(
Json.toJson(
ApiSubscriptionFieldDefinitionsRequest(fieldDefinitions)
)
)
.execute[Either[UpstreamErrorResponse, HttpResponse]]
.map {
case Right(_) => (())
case Left(UpstreamErrorResponse(message, UNPROCESSABLE_ENTITY, _, _)) => throw new UnprocessableEntityException(message)
Expand All @@ -55,10 +57,14 @@ class APISubscriptionFieldsConnector @Inject() (config: ApiSSubscriptionFieldsCo
if (fieldDefinitions.isEmpty) {
Future.successful(None)
} else {
val request = ApiSubscriptionFieldDefinitionsRequest(fieldDefinitions)
val putUrl = s"$serviceBaseUrl/validate"

http.POST[ApiSubscriptionFieldDefinitionsRequest, Either[UpstreamErrorResponse, HttpResponse]](putUrl, request)
http
.post(url"$serviceBaseUrl/validate")
.withBody(
Json.toJson(
ApiSubscriptionFieldDefinitionsRequest(fieldDefinitions)
)
)
.execute[Either[UpstreamErrorResponse, HttpResponse]]
.map {
case Right(_) => None
case Left(UpstreamErrorResponse(message, UNPROCESSABLE_ENTITY, _, _)) => Some(JsString("Field definitions are invalid"))
Expand Down
20 changes: 10 additions & 10 deletions app/uk/gov/hmrc/apipublisher/connectors/MicroserviceConnector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ import play.api.Environment
import play.api.http.Status.NO_CONTENT
import play.api.libs.json.Json
import uk.gov.hmrc.http.HttpReads.Implicits._
import uk.gov.hmrc.http.{HeaderCarrier, HttpClient, HttpReads, HttpReadsOption, HttpResponse}
import uk.gov.hmrc.http.client.HttpClientV2
import uk.gov.hmrc.http.{HeaderCarrier, HttpReads, HttpReadsOption, HttpResponse, StringContextOps}
import uk.gov.hmrc.ramltools.RAML
import uk.gov.hmrc.ramltools.loaders.RamlLoader

Expand All @@ -50,7 +51,7 @@ class MicroserviceConnector @Inject() (
config: MicroserviceConnector.Config,
ramlLoader: RamlLoader,
oasFileLoader: OASFileLoader,
http: HttpClient,
http: HttpClientV2,
env: Environment
)(implicit val ec: ExecutionContext
) extends ConnectorRecovery with HttpReadsOption with ApplicationLogger {
Expand All @@ -76,15 +77,14 @@ class MicroserviceConnector @Inject() (

import uk.gov.hmrc.http.UpstreamErrorResponse

val url = s"${serviceLocation.serviceUrl}/api/definition"

http.GET[Option[ApiAndScopes]](url)(readOptionOfNotFound, implicitly, implicitly)
http
.get(url"${serviceLocation.serviceUrl}/api/definition")
.execute[Either[UpstreamErrorResponse, Option[ApiAndScopes]]] // Uses readOptionOfNotFound for reading
.map {
_.toRight(DefinitionFileNoBodyReturned(serviceLocation))
}
.recover {
case UpstreamErrorResponse(_, NOT_FOUND, _, _) => Left(DefinitionFileNotFound(serviceLocation))
case UpstreamErrorResponse(message, UNPROCESSABLE_ENTITY, _, _) => Left(DefinitionFileUnprocessableEntity(serviceLocation, message))
case Right(oApiAndScopes) => oApiAndScopes.toRight(DefinitionFileNoBodyReturned(serviceLocation))
case Left(UpstreamErrorResponse(_, NOT_FOUND, _, _)) => Left(DefinitionFileNotFound(serviceLocation))
case Left(UpstreamErrorResponse(message, UNPROCESSABLE_ENTITY, _, _)) => Left(DefinitionFileUnprocessableEntity(serviceLocation, message))
case Left(err) => throw err
}
.map(_.map(defaultCategories))
.map(_.flatMap(validateApiAndScopesAgainstSchema))
Expand Down
21 changes: 5 additions & 16 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import play.sbt.PlayScala
import uk.gov.hmrc.DefaultBuildSettings
import uk.gov.hmrc.DefaultBuildSettings._

lazy val appName = "api-publisher"

lazy val playSettings: Seq[Setting[_]] = Seq.empty
Global / bloopAggregateSourceDependencies := true
Global / bloopExportJarClassifiers := Some(Set("sources"))

ThisBuild / scalaVersion := "2.13.12"
ThisBuild / majorVersion := 0
Expand All @@ -15,12 +14,8 @@ ThisBuild / semanticdbVersion := scalafixSemanticdb.revision
lazy val microservice = Project(appName, file("."))
.enablePlugins(PlayScala, SbtDistributablesPlugin)
.disablePlugins(JUnitXmlReportPlugin)
.settings(playSettings: _*)
.settings(scalaSettings: _*)
.settings(defaultSettings(): _*)
.settings(ScoverageSettings())
.settings(
name := appName,
libraryDependencies ++= AppDependencies(),
retrieveManaged := true,
Compile / unmanagedResourceDirectories += baseDirectory.value / "app" / "resources"
Expand All @@ -29,17 +24,14 @@ lazy val microservice = Project(appName, file("."))
Test / testOptions += Tests.Argument(TestFrameworks.ScalaTest, "-eT"),
Test / fork := false,
Test / parallelExecution := false,
Test / unmanagedSourceDirectories += baseDirectory.value / "test",
Test / unmanagedSourceDirectories += baseDirectory.value / "testcommon",
Test / unmanagedResourceDirectories += baseDirectory.value / "test" / "resources",
addTestReportOption(Test, "test-reports")
)
.settings(
scalacOptions ++= Seq(
"-Wconf:cat=unused&src=views/.*\\.scala:s",
"-Wconf:cat=unused&src=.*RoutesPrefix\\.scala:s",
"-Wconf:cat=unused&src=.*Routes\\.scala:s",
"-Wconf:cat=unused&src=.*ReverseRoutes\\.scala:s"
// https://www.scala-lang.org/2021/01/12/configuring-and-suppressing-warnings.html
// suppress warnings in generated routes files
"-Wconf:src=routes/.*:s"
)
)

Expand All @@ -50,12 +42,9 @@ lazy val it = (project in file("it"))
.settings(DefaultBuildSettings.itSettings())
.settings(
name := "integration-tests",
headerSettings(Test) ++ automateHeaderSettings(Test)
)


Global / bloopAggregateSourceDependencies := true
Global / bloopExportJarClassifiers := Some(Set("sources"))


commands ++= Seq(
Expand Down
14 changes: 0 additions & 14 deletions conf/application-json-logger.xml

This file was deleted.

2 changes: 1 addition & 1 deletion conf/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ play.http.errorHandler = "uk.gov.hmrc.apipublisher.wiring.ErrorHandler"

# Define additional modules used here
play.modules.enabled += "uk.gov.hmrc.apipublisher.config.ConfigurationModule"

play.modules.enabled += "uk.gov.hmrc.mongo.play.PlayMongoModule"
play.modules.enabled += "uk.gov.hmrc.play.bootstrap.HttpClientV2Module"

# Session Timeout
# ~~~~
Expand Down
6 changes: 3 additions & 3 deletions project/AppDependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import sbt._
object AppDependencies {
def apply(): Seq[ModuleID] = dependencies ++ testDependencies

lazy val bootstrapVersion = "8.4.0"
lazy val mongoVersion = "1.7.0"
val commonDomainVersion = "0.13.0"
lazy val bootstrapVersion = "9.1.0"
lazy val mongoVersion = "2.1.0"
val commonDomainVersion = "0.15.0"

private lazy val dependencies = Seq(
"uk.gov.hmrc" %% "bootstrap-backend-play-30" % bootstrapVersion,
Expand Down
21 changes: 11 additions & 10 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
resolvers += "HMRC-open-artefacts-maven-2" at "https://open.artefacts.tax.service.gov.uk/maven2"
resolvers += Resolver.url("HMRC-open-artefacts-ivy", url("https://open.artefacts.tax.service.gov.uk/ivy2"))(Resolver.ivyStylePatterns)
resolvers += MavenRepository("HMRC-open-artefacts-maven2", "https://open.artefacts.tax.service.gov.uk/maven2")
resolvers += Resolver.url("HMRC-open-artefacts-ivy2", url("https://open.artefacts.tax.service.gov.uk/ivy2"))(Resolver.ivyStylePatterns)
resolvers += Resolver.typesafeRepo("releases")

addSbtPlugin("uk.gov.hmrc" % "sbt-auto-build" % "3.22.0")
addSbtPlugin("uk.gov.hmrc" % "sbt-distributables" % "2.5.0")
addSbtPlugin("org.playframework" % "sbt-plugin" % "3.0.1")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.9")
addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2")
addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.13")
addSbtPlugin("ch.epfl.scala" %% "sbt-scalafix" % "0.11.1")
addSbtPlugin("uk.gov.hmrc" % "sbt-auto-build" % "3.22.0")
addSbtPlugin("uk.gov.hmrc" % "sbt-distributables" % "2.5.0")
addSbtPlugin("org.playframework" % "sbt-plugin" % "3.0.4")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.9")
addSbtPlugin("org.scalastyle" % "scalastyle-sbt-plugin" % "1.0.0")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2")
addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.15")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.11.1")

ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ import utils.AsyncHmrcSpec
import play.api.Configuration
import play.api.libs.json.{JsObject, Json}
import play.api.test.Helpers.{CONTENT_TYPE, JSON, _}
import uk.gov.hmrc.http.HeaderCarrier
import uk.gov.hmrc.http.HeaderNames.xRequestId
import uk.gov.hmrc.http.{HeaderCarrier, HttpClient}
import uk.gov.hmrc.http.client.HttpClientV2

class APIDefinitionConnectorSpec extends AsyncHmrcSpec with BeforeAndAfterAll with GuiceOneAppPerSuite {

Expand All @@ -52,7 +53,7 @@ class APIDefinitionConnectorSpec extends AsyncHmrcSpec with BeforeAndAfterAll wi

val appConfig: Configuration = mock[Configuration]

val connector = new APIDefinitionConnector(apiDefinitionConfig, app.injector.instanceOf[HttpClient])
val connector = new APIDefinitionConnector(apiDefinitionConfig, app.injector.instanceOf[HttpClientV2])
}

override def beforeAll(): Unit = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ import utils.AsyncHmrcSpec
import play.api.libs.json.Json
import play.api.test.Helpers._
import uk.gov.hmrc.http.HeaderNames.xRequestId
import uk.gov.hmrc.http.{HeaderCarrier, HttpClient, UpstreamErrorResponse}
import uk.gov.hmrc.http.client.HttpClientV2
import uk.gov.hmrc.http.{HeaderCarrier, UpstreamErrorResponse}

import uk.gov.hmrc.apipublisher.models.Scope

Expand All @@ -50,7 +51,7 @@ class APIScopeConnectorSpec extends AsyncHmrcSpec with BeforeAndAfterAll with Gu

implicit val hc: HeaderCarrier = HeaderCarrier().withExtraHeaders(xRequestId -> "requestId")

val connector = new APIScopeConnector(apiScopeConfig, app.injector.instanceOf[HttpClient])
val connector = new APIScopeConnector(apiScopeConfig, app.injector.instanceOf[HttpClientV2])
}

override def beforeAll(): Unit = {
Expand Down
Loading