diff --git a/app/controllers/AddressSearchController.scala b/app/controllers/AddressSearchController.scala index 6304722..8f76221 100644 --- a/app/controllers/AddressSearchController.scala +++ b/app/controllers/AddressSearchController.scala @@ -18,7 +18,8 @@ package controllers import access.AccessChecker import config.ConfigHelper -import model.request.{LookupByCountryRequest, LookupByCountryRequestFilter, LookupByPostTownRequest, LookupByPostcodeRequest, LookupByUprnRequest} +import model.address.Postcode +import model.request.{LookupByCountryRequest, LookupByCountryRequestFilter, LookupByPostTownRequest, LookupByPostcodeRequest, LookupByUprnRequest, LookupRequest} import model.response.{ErrorResponse, SupportedCountryCodes} import play.api.Logging import play.api.libs.json.{JsError, JsSuccess, JsValue, Json, Reads} @@ -37,34 +38,41 @@ class AddressSearchController @Inject()(addressSearchService: AddressSearchServi scheduler: CheckAddressDataScheduler, val configHelper: ConfigHelper)(implicit ec: ExecutionContext) extends BaseController with AccessChecker with Logging { + import ErrorResponse.Implicits._ scheduler.enable() def search(): Action[String] = accessCheckedAction(parse.tolerantText) { request => + implicit val hc: HeaderCarrier = HeaderCarrierConverter.fromRequest(request) + withValidJson[LookupByPostcodeRequest](request) match { - case Left(err) => err + case Left(err) => err case Right(lookupByPostcodeRequest: LookupByPostcodeRequest) => - addressSearchService.searchByPostcode(request, lookupByPostcodeRequest) + doLookup[Postcode, LookupByPostcodeRequest](request, addressSearchService.searchByPostcode(_, _), lookupByPostcodeRequest) } } def searchByUprn(): Action[String] = accessCheckedAction(parse.tolerantText) { request => + implicit val hc: HeaderCarrier = HeaderCarrierConverter.fromRequest(request) + withValidJson[LookupByUprnRequest](request) match { - case Left(err) => err + case Left(err) => err case Right(lookupByUprnRequest: LookupByUprnRequest) => - addressSearchService.searchByUprn(request, lookupByUprnRequest) + doLookup[String, LookupByUprnRequest](request, addressSearchService.searchByUprn(_, _), lookupByUprnRequest) } } def searchByPostTown(): Action[String] = accessCheckedAction(parse.tolerantText) { request => + implicit val hc: HeaderCarrier = HeaderCarrierConverter.fromRequest(request) + withValidJson[LookupByPostTownRequest](request) match { - case Left(err) => err + case Left(err) => err case Right(lookupByPostTownRequest: LookupByPostTownRequest) => - addressSearchService.searchByTown(request, lookupByPostTownRequest) + doLookup[String, LookupByPostTownRequest](request, addressSearchService.searchByTown(_, _), lookupByPostTownRequest) } } @@ -72,28 +80,31 @@ class AddressSearchController @Inject()(addressSearchService: AddressSearchServi request => implicit val hc: HeaderCarrier = HeaderCarrierConverter.fromRequest(request) - withValidJson[LookupByCountryRequestFilter](request, je => Json.toJson(ErrorResponse.invalidJson)) match { + withValidJson[LookupByCountryRequestFilter](request, _ => Json.toJson(ErrorResponse.invalidJson)) match { case Left(err) => err case Right(country: LookupByCountryRequestFilter) => val countryLookup = LookupByCountryRequest.fromLookupByCountryRequestFilter(countryCode.toLowerCase, country) - addressSearchService.searchByCountry(request, countryLookup) + doLookup[String, LookupByCountryRequest](request, addressSearchService.searchByCountry(_, _), countryLookup) } } private def withValidJson[T: Reads](request: Request[String], validateError: JsError => JsValue = err => JsError.toJson(err)): Either[Future[Result], Any] = { - Try(Json.parse(request.body)) match { - case Success(json) => json.validate[T] match { - case JsSuccess(value, _) => + Try(Json.parse(request.body)).toEither match { + case Right(json) => json.validate[T].asEither match { + case Right(value) => Right(value) - case jse@JsError(_) => - Left(Future.successful(BadRequest(validateError(jse)))) + case Left(errs) => + Left(Future.successful(BadRequest(validateError(JsError(errs))))) } - case Failure(_) => Left(Future.successful(BadRequest(Json.toJson(ErrorResponse.invalidJson)))) + case Left(_) => Left(Future.successful(BadRequest(Json.toJson(ErrorResponse.invalidJson)))) } } + private def doLookup[T, B <: LookupRequest[T]](request: Request[String], lookup: (Request[String], B) => Future[Result], lookupRequest: B)(implicit hc: HeaderCarrier) = + lookup(request, lookupRequest) + def supportedCountries(): Action[AnyContent] = Action.async { import model.response.SupportedCountryCodes._ Future.successful(Ok(Json.toJson(supportedCountryCodes))) diff --git a/app/model/request.scala b/app/model/request.scala index 578719a..a77f3e8 100644 --- a/app/model/request.scala +++ b/app/model/request.scala @@ -1,5 +1,5 @@ /* - * Copyright 2023 HM Revenue & Customs + * Copyright 2024 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. @@ -23,9 +23,9 @@ import play.api.libs.json._ object request { - abstract class Filterable(filter: Option[String]) + sealed abstract class LookupRequest[T](value: T, filter: Option[String]) - case class LookupByPostcodeRequest(postcode: Postcode, filter: Option[String] = None) extends Filterable(filter) + case class LookupByPostcodeRequest(postcode: Postcode, filter: Option[String] = None) extends LookupRequest(postcode, filter) object LookupByPostcodeRequest { implicit val postcodeReads: Reads[Postcode] = Reads[Postcode] { json => @@ -39,45 +39,45 @@ object request { } } - implicit val postcodeWrites: Writes[Postcode] = new Writes[Postcode]{ + implicit val postcodeWrites: Writes[Postcode] = new Writes[Postcode] { override def writes(o: Postcode): JsValue = JsString(o.toString) } implicit val reads: Reads[LookupByPostcodeRequest] = ( - (JsPath \ "postcode").read[Postcode] and - (JsPath \ "filter").readNullable[String].map(fo => - fo.flatMap(f => if(f.trim.isEmpty) None else Some(f)) - ) - ) ( + (JsPath \ "postcode").read[Postcode] and + (JsPath \ "filter").readNullable[String].map(fo => + fo.flatMap(f => if (f.trim.isEmpty) None else Some(f)) + ) + )( (pc, fo) => LookupByPostcodeRequest.apply(pc, fo)) implicit val writes: Writes[LookupByPostcodeRequest] = Json.writes[LookupByPostcodeRequest] } - case class LookupByUprnRequest(uprn: String) + case class LookupByUprnRequest(uprn: String) extends LookupRequest(uprn, None) object LookupByUprnRequest { implicit val reads: Reads[LookupByUprnRequest] = Json.reads[LookupByUprnRequest] implicit val writes: Writes[LookupByUprnRequest] = Json.writes[LookupByUprnRequest] } - case class LookupByIdRequest(id: String) + case class LookupByIdRequest(id: String) extends LookupRequest(id, None) object LookupByIdRequest { implicit val reads: Reads[LookupByIdRequest] = Json.reads[LookupByIdRequest] implicit val writes: Writes[LookupByIdRequest] = Json.writes[LookupByIdRequest] } - case class LookupByPostTownRequest(posttown: String, filter: Option[String]) extends Filterable(filter) + case class LookupByPostTownRequest(posttown: String, filter: Option[String]) extends LookupRequest(posttown, filter) object LookupByPostTownRequest { implicit val reads: Reads[LookupByPostTownRequest] = ( - (JsPath \ "posttown").read[String] and - (JsPath \ "filter").readNullable[String].map(fo => - fo.flatMap(f => if(f.trim.isEmpty) None else Some(f)) - ) - ) ( + (JsPath \ "posttown").read[String] and + (JsPath \ "filter").readNullable[String].map(fo => + fo.flatMap(f => if (f.trim.isEmpty) None else Some(f)) + ) + )( (pt, fo) => LookupByPostTownRequest.apply(pt, fo)) implicit val writes: Writes[LookupByPostTownRequest] = Json.writes[LookupByPostTownRequest] @@ -90,7 +90,7 @@ object request { implicit val writes: Writes[LookupByCountryRequestFilter] = Json.writes[LookupByCountryRequestFilter] } - case class LookupByCountryRequest(country: String, filter: Option[String]) + case class LookupByCountryRequest(country: String, filter: Option[String]) extends LookupRequest(country, filter) object LookupByCountryRequest { def fromLookupByCountryRequestFilter(country: String, lookupByCountryRequestFilter: LookupByCountryRequestFilter) =