Skip to content

Hugo/DLS-11581: Scala3 Upgrade #198

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

Merged
merged 6 commits into from
May 23, 2025
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
3 changes: 2 additions & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ style = defaultWithAlign
maxColumn = 120
lineEndings = unix
importSelectors = singleLine
version=2.2.1
runner.dialect = scala3
version = 3.7.13

project {
git = true
Expand Down
4 changes: 2 additions & 2 deletions app/uk/gov/hmrc/helptosaveapi/auth/Auth.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ trait Auth extends AuthorisedFunctions { this: BackendController with Logging =>
def authorised[A](retrievals: Retrieval[A])(action: HtsAction[A])(implicit ec: ExecutionContext): Action[AnyContent] =
Action.async { implicit request =>
authorised(authProviders)
.retrieve(retrievals) { action(request) }
.recover { handleFailure() }
.retrieve(retrievals)(action(request))
.recover(handleFailure())
}

def handleFailure(): PartialFunction[Throwable, Result] = {
Expand Down
36 changes: 19 additions & 17 deletions app/uk/gov/hmrc/helptosaveapi/connectors/HelpToSaveConnector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,46 +19,47 @@ package uk.gov.hmrc.helptosaveapi.connectors
import com.google.inject.{ImplementedBy, Inject, Singleton}
import play.api.Configuration
import play.api.libs.json.{Json, Writes}
import play.api.libs.ws.WSBodyWritables.writeableOf_JsValue
import uk.gov.hmrc.helptosaveapi.connectors.HelpToSaveConnectorImpl.CreateAccountInfo
import uk.gov.hmrc.helptosaveapi.models.ValidateBankDetailsRequest
import uk.gov.hmrc.helptosaveapi.models.createaccount.CreateAccountBody
import uk.gov.hmrc.helptosaveapi.util.Logging
import uk.gov.hmrc.http.{HeaderCarrier, HttpResponse, StringContextOps}
import uk.gov.hmrc.http.HttpReads.Implicits._
import uk.gov.hmrc.http.HttpReads.Implicits.*
import uk.gov.hmrc.http.client.HttpClientV2
import uk.gov.hmrc.http.{HeaderCarrier, HttpResponse, StringContextOps}

import java.util.UUID
import scala.concurrent.{ExecutionContext, Future}

@ImplementedBy(classOf[HelpToSaveConnectorImpl])
trait HelpToSaveConnector {

def createAccount(body: CreateAccountBody, correlationId: UUID, clientCode: String, eligibilityReason: Int)(
implicit hc: HeaderCarrier,
def createAccount(body: CreateAccountBody, correlationId: UUID, clientCode: String, eligibilityReason: Int)(implicit
hc: HeaderCarrier,
ec: ExecutionContext
): Future[HttpResponse]

def checkEligibility(nino: String, correlationId: UUID)(
implicit hc: HeaderCarrier,
def checkEligibility(nino: String, correlationId: UUID)(implicit
hc: HeaderCarrier,
ec: ExecutionContext
): Future[HttpResponse]

def getAccount(nino: String, systemId: String, correlationId: UUID)(
implicit hc: HeaderCarrier,
def getAccount(nino: String, systemId: String, correlationId: UUID)(implicit
hc: HeaderCarrier,
ec: ExecutionContext
): Future[HttpResponse]

def storeEmail(encodedEmail: String, nino: String, correlationId: UUID)(
implicit hc: HeaderCarrier,
def storeEmail(encodedEmail: String, nino: String, correlationId: UUID)(implicit
hc: HeaderCarrier,
ec: ExecutionContext
): Future[HttpResponse]

def validateBankDetails(
request: ValidateBankDetailsRequest
)(implicit hc: HeaderCarrier, ex: ExecutionContext): Future[HttpResponse]

def getUserEnrolmentStatus(nino: String, correlationId: UUID)(
implicit hc: HeaderCarrier,
def getUserEnrolmentStatus(nino: String, correlationId: UUID)(implicit
hc: HeaderCarrier,
ec: ExecutionContext
): Future[HttpResponse]
}
Expand Down Expand Up @@ -87,7 +88,8 @@ class HelpToSaveConnectorImpl @Inject() (config: Configuration, http: HttpClient
val correlationIdHeaderName: String = config.underlying.getString("microservice.correlationIdHeaderName")

override def createAccount(body: CreateAccountBody, correlationId: UUID, clientCode: String, eligibilityReason: Int)(
implicit hc: HeaderCarrier,
implicit
hc: HeaderCarrier,
ec: ExecutionContext
): Future[HttpResponse] = {
val reqBody = CreateAccountInfo(body, eligibilityReason, clientCode)
Expand All @@ -107,8 +109,8 @@ class HelpToSaveConnectorImpl @Inject() (config: Configuration, http: HttpClient
http.get(url"$eligibilityCheckUrl?nino=$nino").transform(_.addHttpHeaders(headers)).execute[HttpResponse]
}

override def getAccount(nino: String, systemId: String, correlationId: UUID)(
implicit hc: HeaderCarrier,
override def getAccount(nino: String, systemId: String, correlationId: UUID)(implicit
hc: HeaderCarrier,
ec: ExecutionContext
): Future[HttpResponse] = {
val url = getAccountUrl(nino)
Expand All @@ -121,8 +123,8 @@ class HelpToSaveConnectorImpl @Inject() (config: Configuration, http: HttpClient
result
}

override def storeEmail(encodedEmail: String, nino: String, correlationId: UUID)(
implicit hc: HeaderCarrier,
override def storeEmail(encodedEmail: String, nino: String, correlationId: UUID)(implicit
hc: HeaderCarrier,
ec: ExecutionContext
): Future[HttpResponse] = {
val headers: (String, String) = s"$correlationIdHeaderName" -> s"$correlationId"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import org.apache.pekko.stream.Materializer
import com.typesafe.config.Config
import controllers.Assets
import play.api.Configuration
import play.api.libs.json.{Format, Json}
import play.api.libs.json.{Json, Reads, Writes}
import play.api.mvc.{Action, AnyContent, ControllerComponents}
import play.filters.cors.CORSActionBuilder
import uk.gov.hmrc.helptosaveapi.controllers.DocumentationController.APIAccess
Expand All @@ -33,7 +33,8 @@ import scala.concurrent.ExecutionContext

@Singleton
class DocumentationController @Inject() (configuration: Configuration, cc: ControllerComponents, assets: Assets)(
implicit materializer: Materializer,
implicit
materializer: Materializer,
executionContext: ExecutionContext
) extends BackendController(cc) {

Expand All @@ -59,7 +60,9 @@ object DocumentationController {

object APIAccess {

implicit val apiAccessFormats: Format[APIAccess] = Json.format[APIAccess]
// implicit val apiAccessFormats: Format[APIAccess] = Json.format[APIAccess]
implicit val apiAccessWrites: Writes[APIAccess] = Json.writes[APIAccess]
implicit val apiAccessReads: Reads[APIAccess] = Json.reads[APIAccess]

type Version = String

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,14 @@ class HelpToSaveController @Inject() (

val correlationIdHeaderName: String = config.underlying.getString("microservice.correlationIdHeaderName")

private val userInfoRetrievals = {
private val userInfoRetrievals =
v2.Retrievals.name and
v2.Retrievals.dateOfBirth and
v2.Retrievals.itmpName and
v2.Retrievals.itmpDateOfBirth and
v2.Retrievals.itmpAddress and
v2.Retrievals.email and
v2.Retrievals.confidenceLevel
}

def apiErrorToResult(e: ApiError): Result = e match {
case _: ApiAccessError => Forbidden(Json.toJson(e))
Expand Down Expand Up @@ -85,21 +84,20 @@ class HelpToSaveController @Inject() (
// we can't do the user retrievals before this point because the user retrievals
// will definitely fail with a 500 response from auth for privileged access
authorised(userInfoRetrievals and v2Nino) { _ =>
{
case ggName ~ dob ~ itmpName ~ itmpDob ~ itmpAddress ~ email ~ confidenceLevel ~ authNino =>
if (confidenceLevel >= ConfidenceLevel.L200) {
val retrievedDetails = RetrievedUserDetails(
authNino,
itmpName.flatMap(_.givenName).orElse(ggName.flatMap(_.name)),
itmpName.flatMap(_.familyName).orElse(ggName.flatMap(_.lastName)),
itmpDob.orElse(dob),
itmpAddress,
email
)
helpToSaveApiService.createAccountUserRestricted(request, retrievedDetails).map(handleResult)
} else {
Future.successful(Unauthorized("Insufficient confidence level"))
}
{ case ggName ~ dob ~ itmpName ~ itmpDob ~ itmpAddress ~ email ~ confidenceLevel ~ authNino =>
if (confidenceLevel >= ConfidenceLevel.L200) {
val retrievedDetails = RetrievedUserDetails(
authNino,
itmpName.flatMap(_.givenName).orElse(ggName.flatMap(_.name)),
itmpName.flatMap(_.familyName).orElse(ggName.flatMap(_.lastName)),
itmpDob.orElse(dob),
itmpAddress,
email
)
helpToSaveApiService.createAccountUserRestricted(request, retrievedDetails).map(handleResult)
} else {
Future.successful(Unauthorized("Insufficient confidence level"))
}
}
}(ec)(request)

Expand Down Expand Up @@ -204,11 +202,10 @@ class HelpToSaveController @Inject() (
}
}

val unsupportedCredentialsProviderResult: Result = {
val unsupportedCredentialsProviderResult: Result =
Forbidden(
Json.toJson(
ApiAccessError("UNSUPPORTED_CREDENTIALS_PROVIDER", "credentials provider not recognised").asInstanceOf[ApiError]
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package uk.gov.hmrc.helptosaveapi.models

import play.api.libs.json._
import play.api.libs.json.*

sealed trait EligibilityResponse

Expand Down
2 changes: 1 addition & 1 deletion app/uk/gov/hmrc/helptosaveapi/models/EnrolmentStatus.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package uk.gov.hmrc.helptosaveapi.models

import play.api.libs.json._
import play.api.libs.json.*
import uk.gov.hmrc.helptosaveapi.models.EnrolmentStatus.{Enrolled, NotEnrolled}

sealed trait EnrolmentStatus {
Expand Down
2 changes: 1 addition & 1 deletion app/uk/gov/hmrc/helptosaveapi/models/HtsAccount.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package uk.gov.hmrc.helptosaveapi.models

import play.api.libs.json.{Format, Json}
import play.api.libs.json.{Format, Json, Reads, Writes}

import java.time.LocalDate

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,25 +85,24 @@ object CreateAccountField {
}

def insertFields(fields: Map[CreateAccountField, JsValue])(json: JsValue): JsValue = // scalastyle:ignore
fields.foldLeft(json.as[JsObject]) {
case (acc, (field, value)) =>
acc.deepMerge(field match {
case Forename => jsObject(NonEmptyList.of("body", "forename") -> value)
case Surname => jsObject(NonEmptyList.of("body", "surname") -> value)
case DateOfBirth => jsObject(NonEmptyList.of("body", "dateOfBirth") -> value)
case NINO => jsObject(NonEmptyList.of("body", "nino") -> value)
case AddressLine1 => jsObject(NonEmptyList.of("body", "contactDetails", "address1") -> value)
case AddressLine2 => jsObject(NonEmptyList.of("body", "contactDetails", "address2") -> value)
case AddressLine3 => jsObject(NonEmptyList.of("body", "contactDetails", "address3") -> value)
case AddressLine4 => jsObject(NonEmptyList.of("body", "contactDetails", "address4") -> value)
case AddressLine5 => jsObject(NonEmptyList.of("body", "contactDetails", "address5") -> value)
case Postcode => jsObject(NonEmptyList.of("body", "contactDetails", "postcode") -> value)
case CountryCode => jsObject(NonEmptyList.of("body", "contactDetails", "countryCode") -> value)
case Email => jsObject(NonEmptyList.of("body", "contactDetails", "email") -> value)
case CommunicationPreference =>
jsObject(NonEmptyList.of("body", "contactDetails", "communicationPreference") -> value)
case RegistrationChannel | Address => JsObject(Map.empty[String, JsValue])
})
fields.foldLeft(json.as[JsObject]) { case (acc, (field, value)) =>
acc.deepMerge(field match {
case Forename => jsObject(NonEmptyList.of("body", "forename") -> value)
case Surname => jsObject(NonEmptyList.of("body", "surname") -> value)
case DateOfBirth => jsObject(NonEmptyList.of("body", "dateOfBirth") -> value)
case NINO => jsObject(NonEmptyList.of("body", "nino") -> value)
case AddressLine1 => jsObject(NonEmptyList.of("body", "contactDetails", "address1") -> value)
case AddressLine2 => jsObject(NonEmptyList.of("body", "contactDetails", "address2") -> value)
case AddressLine3 => jsObject(NonEmptyList.of("body", "contactDetails", "address3") -> value)
case AddressLine4 => jsObject(NonEmptyList.of("body", "contactDetails", "address4") -> value)
case AddressLine5 => jsObject(NonEmptyList.of("body", "contactDetails", "address5") -> value)
case Postcode => jsObject(NonEmptyList.of("body", "contactDetails", "postcode") -> value)
case CountryCode => jsObject(NonEmptyList.of("body", "contactDetails", "countryCode") -> value)
case Email => jsObject(NonEmptyList.of("body", "contactDetails", "email") -> value)
case CommunicationPreference =>
jsObject(NonEmptyList.of("body", "contactDetails", "communicationPreference") -> value)
case RegistrationChannel | Address => JsObject(Map.empty[String, JsValue])
})
}

private def jsObject(s: (NonEmptyList[String], JsValue)): JsObject = {
Expand Down
45 changes: 21 additions & 24 deletions app/uk/gov/hmrc/helptosaveapi/repo/EligibilityStore.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
package uk.gov.hmrc.helptosaveapi.repo

import cats.data.OptionT
import cats.instances.either._
import cats.syntax.either._
import cats.instances.either.*
import cats.syntax.either.*
import com.google.inject.{ImplementedBy, Inject, Singleton}
import play.api.libs.json.{Format, JsValue, Json}
import uk.gov.hmrc.helptosaveapi.models.EligibilityResponse
Expand All @@ -34,12 +34,12 @@ import scala.concurrent.{ExecutionContext, Future}
@ImplementedBy(classOf[MongoEligibilityStore])
trait EligibilityStore {

def get(correlationId: UUID)(
implicit ec: ExecutionContext
def get(correlationId: UUID)(implicit
ec: ExecutionContext
): Future[Either[String, Option[EligibilityResponseWithNINO]]]

def put(correlationId: UUID, eligibility: EligibilityResponse, nino: String)(
implicit ec: ExecutionContext
def put(correlationId: UUID, eligibility: EligibilityResponse, nino: String)(implicit
ec: ExecutionContext
): Future[Either[String, Unit]]

}
Expand All @@ -55,8 +55,8 @@ object EligibilityStore {
}

@Singleton
class MongoEligibilityStore @Inject() (mongoComponent: MongoComponent, servicesConfig: ServicesConfig)(
implicit ec: ExecutionContext
class MongoEligibilityStore @Inject() (mongoComponent: MongoComponent, servicesConfig: ServicesConfig)(implicit
ec: ExecutionContext
) extends EligibilityStore {

private type EitherStringOr[A] = Either[String, A]
Expand All @@ -78,34 +78,31 @@ class MongoEligibilityStore @Inject() (mongoComponent: MongoComponent, servicesC
cache <- OptionT.fromOption[EitherStringOr](maybeCache)
data <- OptionT.fromOption[EitherStringOr](Some(cache.data))
result <- OptionT.liftF[EitherStringOr, EligibilityResponseWithNINO](
(data \ "eligibility")
.validate[EligibilityResponseWithNINO]
.asEither
.leftMap(e => s"Could not parse data: ${e.mkString("; ")}")
)
(data \ "eligibility")
.validate[EligibilityResponseWithNINO]
.asEither
.leftMap(e => s"Could not parse data: ${e.mkString("; ")}")
)
} yield result

response.value
}
.recover {
case e =>
Left(e.getMessage)
.recover { case e =>
Left(e.getMessage)
}

override def put(correlationId: UUID, eligibility: EligibilityResponse, nino: String)(
implicit ec: ExecutionContext
override def put(correlationId: UUID, eligibility: EligibilityResponse, nino: String)(implicit
ec: ExecutionContext
): Future[Either[String, Unit]] =
doCreateOrUpdate(
correlationId.toString,
"eligibility",
Json.toJson(EligibilityResponseWithNINO(eligibility, nino))
).map[Either[String, Unit]] { dbUpdate =>
Right(())
}
.recover {
case e =>
Left(e.getMessage)
}
Right(())
}.recover { case e =>
Left(e.getMessage)
}

private[repo] def doFindById(id: String) =
preservingMdc {
Expand Down
Loading