diff --git a/app/definition/ApiDefinition.scala b/app/definition/ApiDefinition.scala index 8e43180..315d1a1 100644 --- a/app/definition/ApiDefinition.scala +++ b/app/definition/ApiDefinition.scala @@ -69,8 +69,9 @@ case class APIDefinition(name: String, require(versions.nonEmpty, "at least one version is required") require(uniqueVersions, "version numbers must be unique") - private def uniqueVersions = { - !versions.map(_.version).groupBy(identity).mapValues(_.size).exists(_._2 > 1) + private def uniqueVersions: Boolean = { + val foundVersions: Seq[String] = versions.map(_.version) + foundVersions.distinct == foundVersions } } diff --git a/app/utils/ErrorHandler.scala b/app/utils/ErrorHandler.scala index 2f2f281..d241e53 100644 --- a/app/utils/ErrorHandler.scala +++ b/app/utils/ErrorHandler.scala @@ -87,8 +87,8 @@ class ErrorHandler @Inject()(config: Configuration, case _: JsValidationException => (BAD_REQUEST, BadRequestError, "ServerValidationError") case e: HttpException => (e.responseCode, BadRequestError, "ServerValidationError") case e: UpstreamErrorResponse if UpstreamErrorResponse.Upstream4xxResponse.unapply(e).isDefined => (e.reportAs, BadRequestError, "ServerValidationError") - case e: UpstreamErrorResponse if UpstreamErrorResponse.Upstream5xxResponse.unapply(e).isDefined => (e.reportAs, DownstreamError, "ServerInternalError") - case _ => (INTERNAL_SERVER_ERROR, DownstreamError, "ServerInternalError") + case e: UpstreamErrorResponse if UpstreamErrorResponse.Upstream5xxResponse.unapply(e).isDefined => (e.reportAs, InternalError, "ServerInternalError") + case _ => (INTERNAL_SERVER_ERROR, InternalError, "ServerInternalError") } auditConnector.sendEvent( diff --git a/app/v1/connectors/AmendDisclosuresConnector.scala b/app/v1/connectors/AmendDisclosuresConnector.scala index 294cf16..b035020 100644 --- a/app/v1/connectors/AmendDisclosuresConnector.scala +++ b/app/v1/connectors/AmendDisclosuresConnector.scala @@ -33,7 +33,7 @@ class AmendDisclosuresConnector @Inject()(val http: HttpClient, ec: ExecutionContext, correlationId: String): Future[DownstreamOutcome[Unit]] = { - import v1.connectors.httpparsers.StandardDesHttpParser._ + import v1.connectors.httpparsers.StandardDownstreamHttpParser._ val nino = request.nino.nino val taxYear = request.taxYear diff --git a/app/v1/connectors/CreateMarriageAllowanceConnector.scala b/app/v1/connectors/CreateMarriageAllowanceConnector.scala index 0adc99e..03b8a8a 100644 --- a/app/v1/connectors/CreateMarriageAllowanceConnector.scala +++ b/app/v1/connectors/CreateMarriageAllowanceConnector.scala @@ -33,7 +33,7 @@ class CreateMarriageAllowanceConnector @Inject()(val http: HttpClient, ec: ExecutionContext, correlationId: String): Future[DownstreamOutcome[Unit]] = { - import v1.connectors.httpparsers.StandardDesHttpParser._ + import v1.connectors.httpparsers.StandardDownstreamHttpParser._ val nino = request.nino.nino diff --git a/app/v1/connectors/DeleteRetrieveConnector.scala b/app/v1/connectors/DeleteRetrieveConnector.scala index abb4650..8581d87 100644 --- a/app/v1/connectors/DeleteRetrieveConnector.scala +++ b/app/v1/connectors/DeleteRetrieveConnector.scala @@ -33,7 +33,7 @@ class DeleteRetrieveConnector @Inject()(val http: HttpClient, ifs1Uri: Ifs1Uri[Unit], correlationId: String): Future[DownstreamOutcome[Unit]] = { - import v1.connectors.httpparsers.StandardDesHttpParser._ + import v1.connectors.httpparsers.StandardDownstreamHttpParser._ delete(uri = ifs1Uri) } @@ -43,7 +43,7 @@ class DeleteRetrieveConnector @Inject()(val http: HttpClient, ifs1Uri: Ifs1Uri[Resp], correlationId: String): Future[DownstreamOutcome[Resp]] = { - import v1.connectors.httpparsers.StandardDesHttpParser._ + import v1.connectors.httpparsers.StandardDownstreamHttpParser._ get(uri = ifs1Uri) } diff --git a/app/v1/connectors/DownstreamUri.scala b/app/v1/connectors/DownstreamUri.scala index 5829a45..9505528 100644 --- a/app/v1/connectors/DownstreamUri.scala +++ b/app/v1/connectors/DownstreamUri.scala @@ -16,11 +16,11 @@ package v1.connectors -trait DownstreamUri[Resp] { +sealed trait DownstreamUri[Resp] { val value: String } object DownstreamUri { - case class Ifs1Uri[Resp](value: String) extends DownstreamUri[Resp] - case class Ifs2Uri[Resp](value: String) extends DownstreamUri[Resp] + final case class Ifs1Uri[Resp](value: String) extends DownstreamUri[Resp] + final case class Ifs2Uri[Resp](value: String) extends DownstreamUri[Resp] } diff --git a/app/v1/connectors/httpparsers/HttpParser.scala b/app/v1/connectors/httpparsers/HttpParser.scala index 55e8a0e..6d198c9 100644 --- a/app/v1/connectors/httpparsers/HttpParser.scala +++ b/app/v1/connectors/httpparsers/HttpParser.scala @@ -24,7 +24,6 @@ import v1.models.errors._ import scala.util.{Success, Try} trait HttpParser { - private val logger: Logger = Logger(this.getClass) implicit class KnownJsonResponse(response: HttpResponse) { @@ -39,7 +38,6 @@ trait HttpParser { } def parseResult[T](json: JsValue)(implicit reads: Reads[T]): Option[T] = json.validate[T] match { - case JsSuccess(value, _) => Some(value) case JsError(error) => logger.warn(s"[KnownJsonResponse][validateJson] Unable to parse JSON: $error") @@ -49,23 +47,22 @@ trait HttpParser { def retrieveCorrelationId(response: HttpResponse): String = response.header("CorrelationId").getOrElse("") - private val multipleErrorReads: Reads[List[DesErrorCode]] = (__ \ "failures").read[List[DesErrorCode]] + private val multipleErrorReads: Reads[List[DownstreamErrorCode]] = (__ \ "failures").read[List[DownstreamErrorCode]] - private val bvrErrorReads: Reads[Seq[DesErrorCode]] = { - implicit val errorIdReads: Reads[DesErrorCode] = (__ \ "id").read[String].map(DesErrorCode(_)) - (__ \ "bvrfailureResponseElement" \ "validationRuleFailures").read[Seq[DesErrorCode]] + private val bvrErrorReads: Reads[List[DownstreamErrorCode]] = { + implicit val errorIdReads: Reads[DownstreamErrorCode] = (__ \ "id").read[String].map(DownstreamErrorCode(_)) + (__ \ "bvrfailureResponseElement" \ "validationRuleFailures").read[List[DownstreamErrorCode]] } - def parseErrors(response: HttpResponse): DesError = { - val singleError = response.validateJson[DesErrorCode].map(err => DesErrors(List(err))) - lazy val multipleErrors = response.validateJson(multipleErrorReads).map(errs => DesErrors(errs)) + def parseErrors(response: HttpResponse): DownstreamError = { + val singleError = response.validateJson[DownstreamErrorCode].map(err => DownstreamErrors(List(err))) + lazy val multipleErrors = response.validateJson(multipleErrorReads).map(errs => DownstreamErrors(errs)) lazy val bvrErrors = response.validateJson(bvrErrorReads).map(errs => OutboundError(BVRError, Some(errs.map(_.toMtd)))) lazy val unableToParseJsonError = { logger.warn(s"unable to parse errors from response: ${response.body}") - OutboundError(DownstreamError) + OutboundError(InternalError) } singleError orElse multipleErrors orElse bvrErrors getOrElse unableToParseJsonError } - } diff --git a/app/v1/connectors/httpparsers/MtdIdLookupHttpParser.scala b/app/v1/connectors/httpparsers/MtdIdLookupHttpParser.scala index b1824f6..ea9d766 100644 --- a/app/v1/connectors/httpparsers/MtdIdLookupHttpParser.scala +++ b/app/v1/connectors/httpparsers/MtdIdLookupHttpParser.scala @@ -20,7 +20,7 @@ import play.api.http.Status.{FORBIDDEN, OK, UNAUTHORIZED} import play.api.libs.json._ import uk.gov.hmrc.http.{HttpReads, HttpResponse} import v1.connectors.MtdIdLookupOutcome -import v1.models.errors.{DownstreamError, InvalidBearerTokenError, NinoFormatError} +import v1.models.errors.{InternalError, InvalidBearerTokenError, NinoFormatError} object MtdIdLookupHttpParser extends HttpParser { @@ -30,11 +30,11 @@ object MtdIdLookupHttpParser extends HttpParser { response.status match { case OK => response.validateJson[String](mtdIdJsonReads) match { case Some(mtdId) => Right(mtdId) - case None => Left(DownstreamError) + case None => Left(InternalError) } case FORBIDDEN => Left(NinoFormatError) case UNAUTHORIZED => Left(InvalidBearerTokenError) - case _ => Left(DownstreamError) + case _ => Left(InternalError) } } } diff --git a/app/v1/connectors/httpparsers/StandardDesHttpParser.scala b/app/v1/connectors/httpparsers/StandardDownstreamHttpParser.scala similarity index 79% rename from app/v1/connectors/httpparsers/StandardDesHttpParser.scala rename to app/v1/connectors/httpparsers/StandardDownstreamHttpParser.scala index 8137075..b5f21c2 100644 --- a/app/v1/connectors/httpparsers/StandardDesHttpParser.scala +++ b/app/v1/connectors/httpparsers/StandardDownstreamHttpParser.scala @@ -21,16 +21,16 @@ import play.api.http.Status._ import play.api.libs.json.Reads import uk.gov.hmrc.http.{HttpReads, HttpResponse} import v1.connectors.DownstreamOutcome -import v1.models.errors.{DownstreamError, OutboundError} +import v1.models.errors.{InternalError, OutboundError} import v1.models.outcomes.ResponseWrapper -object StandardDesHttpParser extends HttpParser { +object StandardDownstreamHttpParser extends HttpParser { case class SuccessCode(status: Int) extends AnyVal val logger: Logger = Logger(getClass) - // Return Right[DesResponse[Unit]] as success response has no body - no need to assign it a value + // Return Right[DownstreamOutcome[Unit]] as success response has no body - no need to assign it a value implicit def readsEmpty(implicit successCode: SuccessCode = SuccessCode(NO_CONTENT)): HttpReads[DownstreamOutcome[Unit]] = (_: String, url: String, response: HttpResponse) => doRead(url, response) { correlationId => Right(ResponseWrapper(correlationId, ())) @@ -40,7 +40,7 @@ object StandardDesHttpParser extends HttpParser { (_: String, url: String, response: HttpResponse) => doRead(url, response) { correlationId => response.validateJson[A] match { case Some(ref) => Right(ResponseWrapper(correlationId, ref)) - case None => Left(ResponseWrapper(correlationId, OutboundError(DownstreamError))) + case None => Left(ResponseWrapper(correlationId, OutboundError(InternalError))) } } @@ -51,19 +51,19 @@ object StandardDesHttpParser extends HttpParser { if (response.status != successCode.status) { logger.warn( - "[StandardDesHttpParser][read] - " + - s"Error response received from DES with status: ${response.status} and body\n" + + "[StandardDownstreamHttpParser][read] - " + + s"Error response received from Downstream with status: ${response.status} and body\n" + s"${response.body} and correlationId: $correlationId when calling $url") } response.status match { case successCode.status => logger.info( - "[StandardDesHttpParser][read] - " + - s"Success response received from DES with correlationId: $correlationId when calling $url") + "[StandardDownstreamHttpParser][read] - " + + s"Success response received from Downstream with correlationId: $correlationId when calling $url") successOutcomeFactory(correlationId) case BAD_REQUEST | NOT_FOUND | FORBIDDEN | CONFLICT | UNPROCESSABLE_ENTITY => Left(ResponseWrapper(correlationId, parseErrors(response))) - case _ => Left(ResponseWrapper(correlationId, OutboundError(DownstreamError))) + case _ => Left(ResponseWrapper(correlationId, OutboundError(InternalError))) } } } diff --git a/app/v1/connectors/package.scala b/app/v1/connectors/package.scala index 45ba4eb..9f11279 100644 --- a/app/v1/connectors/package.scala +++ b/app/v1/connectors/package.scala @@ -16,11 +16,11 @@ package v1 -import v1.models.errors.{DesError, MtdError} +import v1.models.errors.{DownstreamError, MtdError} import v1.models.outcomes.ResponseWrapper package object connectors { type MtdIdLookupOutcome = Either[MtdError, String] - type DownstreamOutcome[A] = Either[ResponseWrapper[DesError], ResponseWrapper[A]] + type DownstreamOutcome[A] = Either[ResponseWrapper[DownstreamError], ResponseWrapper[A]] } diff --git a/app/v1/controllers/AmendDisclosuresController.scala b/app/v1/controllers/AmendDisclosuresController.scala index c7a5baf..bd9f605 100644 --- a/app/v1/controllers/AmendDisclosuresController.scala +++ b/app/v1/controllers/AmendDisclosuresController.scala @@ -73,15 +73,15 @@ class AmendDisclosuresController @Inject()(val authService: EnrolmentsAuthServic } yield { logger.info( s"[${endpointLogContext.controllerName}][${endpointLogContext.endpointName}] - " + - s"Success response received with CorrelationId: ${serviceResponse.correlationId}") + s"Success response received with CorrelationId: ${serviceResponse.correlationId}" + ) val hateoasResponse = amendDisclosuresHateoasBody(appConfig, nino, taxYear) - auditSubmission( - GenericAuditDetail(request.userDetails, Map("nino" -> nino, "taxYear" -> taxYear), Some(request.body), - serviceResponse.correlationId, AuditResponse(httpStatus = OK, response = Right(Some(hateoasResponse))) - ) - ) + auditSubmission(GenericAuditDetail( + request.userDetails, Map("nino" -> nino, "taxYear" -> taxYear), Some(request.body), + serviceResponse.correlationId, AuditResponse(httpStatus = OK, response = Right(Some(hateoasResponse))) + )) Ok(hateoasResponse) .withApiHeaders(serviceResponse.correlationId) @@ -93,14 +93,14 @@ class AmendDisclosuresController @Inject()(val authService: EnrolmentsAuthServic val result = errorResult(errorWrapper).withApiHeaders(resCorrelationId) logger.warn( s"[${endpointLogContext.controllerName}][${endpointLogContext.endpointName}] - " + - s"Error response received with CorrelationId: $resCorrelationId") - - auditSubmission( - GenericAuditDetail(request.userDetails, Map("nino" -> nino, "taxYear" -> taxYear), Some(request.body), - resCorrelationId, AuditResponse(httpStatus = result.header.status, response = Left(errorWrapper.auditErrors)) - ) + s"Error response received with CorrelationId: $resCorrelationId" ) + auditSubmission(GenericAuditDetail( + request.userDetails, Map("nino" -> nino, "taxYear" -> taxYear), Some(request.body), resCorrelationId, + AuditResponse(httpStatus = result.header.status, response = Left(errorWrapper.auditErrors)) + )) + result }.merge } @@ -109,14 +109,12 @@ class AmendDisclosuresController @Inject()(val authService: EnrolmentsAuthServic (errorWrapper.error: @unchecked) match { case BadRequestError | NinoFormatError | TaxYearFormatError | RuleTaxYearNotSupportedError | RuleTaxYearRangeInvalidError | MtdErrorWithCustomMessage(RuleIncorrectOrEmptyBodyError.code) | - MtdErrorWithCustomMessage(SRNFormatError.code) | - MtdErrorWithCustomMessage(TaxYearFormatError.code) | + MtdErrorWithCustomMessage(SRNFormatError.code) | MtdErrorWithCustomMessage(TaxYearFormatError.code) | MtdErrorWithCustomMessage(RuleTaxYearRangeInvalidError.code) | - MtdErrorWithCustomMessage(RuleVoluntaryClass2ValueInvalidError.code) - => BadRequest(Json.toJson(errorWrapper)) + MtdErrorWithCustomMessage(RuleVoluntaryClass2ValueInvalidError.code) => BadRequest(Json.toJson(errorWrapper)) case RuleVoluntaryClass2CannotBeChangedError => Forbidden(Json.toJson(errorWrapper)) case NotFoundError => NotFound(Json.toJson(errorWrapper)) - case DownstreamError => InternalServerError(Json.toJson(errorWrapper)) + case InternalError => InternalServerError(Json.toJson(errorWrapper)) } } diff --git a/app/v1/controllers/AuthorisedController.scala b/app/v1/controllers/AuthorisedController.scala index 946590d..802413d 100644 --- a/app/v1/controllers/AuthorisedController.scala +++ b/app/v1/controllers/AuthorisedController.scala @@ -49,22 +49,21 @@ abstract class AuthorisedController(cc: ControllerComponents)(implicit ec: Execu def invokeBlockWithAuthCheck[A](mtdId: String, request: Request[A], block: UserRequest[A] => Future[Result])( implicit headerCarrier: HeaderCarrier): Future[Result] = { authService.authorised(predicate(mtdId)).flatMap[Result] { - case Right(userDetails) => block(UserRequest(userDetails.copy(mtdId = mtdId), request)) + case Right(userDetails) => block(UserRequest(userDetails.copy(mtdId = mtdId), request)) case Left(UnauthorisedError) => Future.successful(Forbidden(Json.toJson(UnauthorisedError))) - case Left(_) => Future.successful(InternalServerError(Json.toJson(DownstreamError))) + case Left(_) => Future.successful(InternalServerError(Json.toJson(InternalError))) } } override def invokeBlock[A](request: Request[A], block: UserRequest[A] => Future[Result]): Future[Result] = { - implicit val headerCarrier: HeaderCarrier = hc(request) lookupService.lookup(nino).flatMap[Result] { - case Right(mtdId) => invokeBlockWithAuthCheck(mtdId, request, block) - case Left(NinoFormatError) => Future.successful(BadRequest(Json.toJson(NinoFormatError))) + case Right(mtdId) => invokeBlockWithAuthCheck(mtdId, request, block) + case Left(NinoFormatError) => Future.successful(BadRequest(Json.toJson(NinoFormatError))) case Left(UnauthorisedError) => Future.successful(Forbidden(Json.toJson(UnauthorisedError))) case Left(InvalidBearerTokenError) => Future.successful(Unauthorized(Json.toJson(InvalidBearerTokenError))) - case Left(_) => Future.successful(InternalServerError(Json.toJson(DownstreamError))) + case Left(_) => Future.successful(InternalServerError(Json.toJson(InternalError))) } } } diff --git a/app/v1/controllers/BaseController.scala b/app/v1/controllers/BaseController.scala index 6600789..7e85e95 100644 --- a/app/v1/controllers/BaseController.scala +++ b/app/v1/controllers/BaseController.scala @@ -23,7 +23,6 @@ trait BaseController { self: Logging => implicit class Response(result: Result) { - def withApiHeaders(correlationId: String, responseHeaders: (String, String)*): Result = { val newHeaders: Seq[(String, String)] = responseHeaders ++ Seq( diff --git a/app/v1/controllers/CreateMarriageAllowanceController.scala b/app/v1/controllers/CreateMarriageAllowanceController.scala index 3db274c..55c1f3a 100644 --- a/app/v1/controllers/CreateMarriageAllowanceController.scala +++ b/app/v1/controllers/CreateMarriageAllowanceController.scala @@ -119,7 +119,7 @@ class CreateMarriageAllowanceController @Inject()(val authService: EnrolmentsAut case RuleDeceasedRecipientError | RuleInvalidRequestError | RuleActiveMarriageAllowanceClaimError => Forbidden(Json.toJson(errorWrapper)) - case DownstreamError => InternalServerError(Json.toJson(errorWrapper)) + case InternalError => InternalServerError(Json.toJson(errorWrapper)) } } diff --git a/app/v1/controllers/DeleteDisclosuresController.scala b/app/v1/controllers/DeleteDisclosuresController.scala index a091938..ac2b48b 100644 --- a/app/v1/controllers/DeleteDisclosuresController.scala +++ b/app/v1/controllers/DeleteDisclosuresController.scala @@ -18,7 +18,6 @@ package v1.controllers import cats.data.EitherT import cats.implicits._ -import javax.inject.{Inject, Singleton} import play.api.libs.json.Json import play.api.mvc.{Action, AnyContent, ControllerComponents} import play.mvc.Http.MimeTypes @@ -32,6 +31,7 @@ import v1.models.errors._ import v1.models.request.DeleteRetrieveRawData import v1.services.{AuditService, DeleteRetrieveService, EnrolmentsAuthService, MtdIdLookupService} +import javax.inject.{Inject, Singleton} import scala.concurrent.{ExecutionContext, Future} @Singleton @@ -56,7 +56,8 @@ class DeleteDisclosuresController @Inject()(val authService: EnrolmentsAuthServi implicit val correlationId: String = idGenerator.generateCorrelationId logger.info( s"[${endpointLogContext.controllerName}][${endpointLogContext.endpointName}] " + - s"with CorrelationId: $correlationId") + s"with CorrelationId: $correlationId" + ) val rawData: DeleteRetrieveRawData = DeleteRetrieveRawData( nino = nino, @@ -72,18 +73,19 @@ class DeleteDisclosuresController @Inject()(val authService: EnrolmentsAuthServi } yield { logger.info( s"[${endpointLogContext.controllerName}][${endpointLogContext.endpointName}] - " + - s"Success response received with CorrelationId: ${serviceResponse.correlationId}") - - auditSubmission( - GenericAuditDetail( - request.userDetails, Map("nino" -> nino, "taxYear" -> taxYear), None, - serviceResponse.correlationId, AuditResponse(httpStatus = NO_CONTENT, response = Right(None)) - ) + s"Success response received with CorrelationId: ${serviceResponse.correlationId}" ) + + auditSubmission(GenericAuditDetail( + request.userDetails, Map("nino" -> nino, "taxYear" -> taxYear), None, + serviceResponse.correlationId, AuditResponse(httpStatus = NO_CONTENT, response = Right(None)) + )) + NoContent .withApiHeaders(serviceResponse.correlationId) .as(MimeTypes.JSON) } + result.leftMap { errorWrapper => val resCorrelationId = errorWrapper.correlationId val result = errorResult(errorWrapper).withApiHeaders(resCorrelationId) @@ -91,12 +93,11 @@ class DeleteDisclosuresController @Inject()(val authService: EnrolmentsAuthServi s"[${endpointLogContext.controllerName}][${endpointLogContext.endpointName}] - " + s"Error response received with CorrelationId: $resCorrelationId") - auditSubmission( - GenericAuditDetail( - request.userDetails, Map("nino" -> nino, "taxYear" -> taxYear), None, - resCorrelationId, AuditResponse(httpStatus = result.header.status, response = Left(errorWrapper.auditErrors)) - ) - ) + auditSubmission(GenericAuditDetail( + request.userDetails, Map("nino" -> nino, "taxYear" -> taxYear), None, + resCorrelationId, AuditResponse(httpStatus = result.header.status, response = Left(errorWrapper.auditErrors)) + )) + result }.merge } @@ -107,20 +108,19 @@ class DeleteDisclosuresController @Inject()(val authService: EnrolmentsAuthServi RuleTaxYearRangeInvalidError => BadRequest(Json.toJson(errorWrapper)) case RuleVoluntaryClass2CannotBeChangedError => Forbidden(Json.toJson(errorWrapper)) case NotFoundError => NotFound(Json.toJson(errorWrapper)) - case DownstreamError => InternalServerError(Json.toJson(errorWrapper)) + case InternalError => InternalServerError(Json.toJson(errorWrapper)) } } - private def ifsErrorMap: Map[String, MtdError] = - Map( - "INVALID_TAXABLE_ENTITY_ID" -> NinoFormatError, - "INVALID_TAX_YEAR" -> TaxYearFormatError, - "INVALID_CORRELATIONID" -> DownstreamError, - "NO_DATA_FOUND" -> NotFoundError, - "VOLUNTARY_CLASS2_CANNOT_BE_CHANGED" -> RuleVoluntaryClass2CannotBeChangedError, - "SERVER_ERROR" -> DownstreamError, - "SERVICE_UNAVAILABLE" -> DownstreamError - ) + private def ifsErrorMap: Map[String, MtdError] = Map( + "INVALID_TAXABLE_ENTITY_ID" -> NinoFormatError, + "INVALID_TAX_YEAR" -> TaxYearFormatError, + "INVALID_CORRELATIONID" -> InternalError, + "NO_DATA_FOUND" -> NotFoundError, + "VOLUNTARY_CLASS2_CANNOT_BE_CHANGED" -> RuleVoluntaryClass2CannotBeChangedError, + "SERVER_ERROR" -> InternalError, + "SERVICE_UNAVAILABLE" -> InternalError + ) private def auditSubmission(details: GenericAuditDetail) (implicit hc: HeaderCarrier, diff --git a/app/v1/controllers/EndpointLogContext.scala b/app/v1/controllers/EndpointLogContext.scala index 715a486..4c700a7 100644 --- a/app/v1/controllers/EndpointLogContext.scala +++ b/app/v1/controllers/EndpointLogContext.scala @@ -17,7 +17,5 @@ package v1.controllers -case class EndpointLogContext( - controllerName: String, - endpointName: String - ) +case class EndpointLogContext(controllerName: String, + endpointName: String) diff --git a/app/v1/controllers/RetrieveDisclosuresController.scala b/app/v1/controllers/RetrieveDisclosuresController.scala index b2e29a5..01fd85e 100644 --- a/app/v1/controllers/RetrieveDisclosuresController.scala +++ b/app/v1/controllers/RetrieveDisclosuresController.scala @@ -55,7 +55,8 @@ class RetrieveDisclosuresController @Inject()(val authService: EnrolmentsAuthSer implicit val correlationId: String = idGenerator.generateCorrelationId logger.info( s"[${endpointLogContext.controllerName}][${endpointLogContext.endpointName}] " + - s"with CorrelationId: $correlationId") + s"with CorrelationId: $correlationId" + ) val rawData: DeleteRetrieveRawData = DeleteRetrieveRawData( nino = nino, @@ -77,7 +78,8 @@ class RetrieveDisclosuresController @Inject()(val authService: EnrolmentsAuthSer } yield { logger.info( s"[${endpointLogContext.controllerName}][${endpointLogContext.endpointName}] - " + - s"Success response received with CorrelationId: ${serviceResponse.correlationId}") + s"Success response received with CorrelationId: ${serviceResponse.correlationId}" + ) Ok(Json.toJson(vendorResponse)) .withApiHeaders(serviceResponse.correlationId) @@ -89,7 +91,8 @@ class RetrieveDisclosuresController @Inject()(val authService: EnrolmentsAuthSer val result = errorResult(errorWrapper).withApiHeaders(resCorrelationId) logger.warn( s"[${endpointLogContext.controllerName}][${endpointLogContext.endpointName}] - " + - s"Error response received with CorrelationId: $resCorrelationId") + s"Error response received with CorrelationId: $resCorrelationId" + ) result }.merge @@ -100,7 +103,7 @@ class RetrieveDisclosuresController @Inject()(val authService: EnrolmentsAuthSer case BadRequestError | NinoFormatError | TaxYearFormatError | RuleTaxYearNotSupportedError | RuleTaxYearRangeInvalidError => BadRequest(Json.toJson(errorWrapper)) case NotFoundError => NotFound(Json.toJson(errorWrapper)) - case DownstreamError => InternalServerError(Json.toJson(errorWrapper)) + case InternalError => InternalServerError(Json.toJson(errorWrapper)) } } } diff --git a/app/v1/controllers/requestParsers/AmendDisclosuresRequestParser.scala b/app/v1/controllers/requestParsers/AmendDisclosuresRequestParser.scala index 17d9824..85638db 100644 --- a/app/v1/controllers/requestParsers/AmendDisclosuresRequestParser.scala +++ b/app/v1/controllers/requestParsers/AmendDisclosuresRequestParser.scala @@ -16,14 +16,15 @@ package v1.controllers.requestParsers -import javax.inject.Inject -import v1.models.domain.Nino import v1.controllers.requestParsers.validators.AmendDisclosuresValidator +import v1.models.domain.Nino import v1.models.request.disclosures.{AmendDisclosuresRawData, AmendDisclosuresRequest, AmendDisclosuresRequestBody} +import javax.inject.Inject + class AmendDisclosuresRequestParser @Inject()(val validator: AmendDisclosuresValidator) extends RequestParser[AmendDisclosuresRawData, AmendDisclosuresRequest] { override protected def requestFor(data: AmendDisclosuresRawData): AmendDisclosuresRequest = AmendDisclosuresRequest(Nino(data.nino), data.taxYear, data.body.json.as[AmendDisclosuresRequestBody]) -} \ No newline at end of file +} diff --git a/app/v1/controllers/requestParsers/CreateMarriageAllowanceRequestParser.scala b/app/v1/controllers/requestParsers/CreateMarriageAllowanceRequestParser.scala index a6856b4..6683e1b 100644 --- a/app/v1/controllers/requestParsers/CreateMarriageAllowanceRequestParser.scala +++ b/app/v1/controllers/requestParsers/CreateMarriageAllowanceRequestParser.scala @@ -27,4 +27,4 @@ class CreateMarriageAllowanceRequestParser @Inject()(val validator: CreateMarria override protected def requestFor(data: CreateMarriageAllowanceRawData): CreateMarriageAllowanceRequest = CreateMarriageAllowanceRequest(Nino(data.nino), data.body.json.as[CreateMarriageAllowanceBody]) -} \ No newline at end of file +} diff --git a/app/v1/controllers/requestParsers/DeleteRetrieveRequestParser.scala b/app/v1/controllers/requestParsers/DeleteRetrieveRequestParser.scala index b5ae561..fc7df56 100644 --- a/app/v1/controllers/requestParsers/DeleteRetrieveRequestParser.scala +++ b/app/v1/controllers/requestParsers/DeleteRetrieveRequestParser.scala @@ -16,14 +16,15 @@ package v1.controllers.requestParsers -import javax.inject.Inject -import v1.models.domain.Nino import v1.controllers.requestParsers.validators.DeleteRetrieveValidator +import v1.models.domain.Nino import v1.models.request.{DeleteRetrieveRawData, DeleteRetrieveRequest} +import javax.inject.Inject + class DeleteRetrieveRequestParser @Inject()(val validator: DeleteRetrieveValidator) extends RequestParser[DeleteRetrieveRawData, DeleteRetrieveRequest] { override protected def requestFor(data: DeleteRetrieveRawData): DeleteRetrieveRequest = DeleteRetrieveRequest(Nino(data.nino), data.taxYear) -} \ No newline at end of file +} diff --git a/app/v1/controllers/requestParsers/RequestParser.scala b/app/v1/controllers/requestParsers/RequestParser.scala index ffe4110..53da472 100644 --- a/app/v1/controllers/requestParsers/RequestParser.scala +++ b/app/v1/controllers/requestParsers/RequestParser.scala @@ -18,17 +18,19 @@ package v1.controllers.requestParsers import utils.Logging import v1.controllers.requestParsers.validators.Validator -import v1.models.errors.{BadRequestError, ErrorWrapper} +import v1.models.errors.{BadRequestError, ErrorWrapper, MtdError} import v1.models.request.RawData -trait RequestParser[Raw <: RawData, Request] extends Logging { +trait RequestParser[Raw <: RawData, Request] extends Logging { val validator: Validator[Raw] protected def requestFor(data: Raw): Request def parseRequest(data: Raw)(implicit correlationId: String): Either[ErrorWrapper, Request] = { - validator.validate(data) match { + val validationResult: List[MtdError] = validator.validate(data) + + validationResult match { case Nil => logger.info( "[RequestParser][parseRequest] " + @@ -39,11 +41,11 @@ trait RequestParser[Raw <: RawData, Request] extends Logging { "[RequestParser][parseRequest] " + s"Validation failed with ${err.code} error for the request with CorrelationId: $correlationId") Left(ErrorWrapper(correlationId, err, None)) - case errs => + case _ => logger.warn( "[RequestParser][parseRequest] " + - s"Validation failed with ${errs.map(_.code).mkString(",")} error for the request with CorrelationId: $correlationId") - Left(ErrorWrapper(correlationId, BadRequestError, Some(errs))) + s"Validation failed with ${validationResult.map(_.code).mkString(",")} error for the request with CorrelationId: $correlationId") + Left(ErrorWrapper(correlationId, BadRequestError, Some(validationResult))) } } } diff --git a/app/v1/controllers/requestParsers/validators/AmendDisclosuresValidator.scala b/app/v1/controllers/requestParsers/validators/AmendDisclosuresValidator.scala index a613b3e..afff941 100644 --- a/app/v1/controllers/requestParsers/validators/AmendDisclosuresValidator.scala +++ b/app/v1/controllers/requestParsers/validators/AmendDisclosuresValidator.scala @@ -17,68 +17,60 @@ package v1.controllers.requestParsers.validators import config.AppConfig -import javax.inject.Inject import v1.controllers.requestParsers.validators.validations._ import v1.models.errors.MtdError import v1.models.request.disclosures._ -class AmendDisclosuresValidator @Inject()(implicit appConfig: AppConfig) extends Validator[AmendDisclosuresRawData] { - - private val validationSet = List(parameterFormatValidation, parameterRuleValidation, bodyFormatValidator, bodyValueValidator) +import javax.inject.Inject - override def validate(data: AmendDisclosuresRawData): List[MtdError] = { - run(validationSet, data).distinct - } +class AmendDisclosuresValidator @Inject()(implicit appConfig: AppConfig) extends Validator[AmendDisclosuresRawData] { + private val validationSet = List( + parameterFormatValidation, + parameterRuleValidation, + bodyFormatValidator, + bodyValueValidator + ) - private def parameterFormatValidation: AmendDisclosuresRawData => List[List[MtdError]] = (data: AmendDisclosuresRawData) => { - List( - NinoValidation.validate(data.nino), - TaxYearValidation.validate(data.taxYear) - ) - } + override def validate(data: AmendDisclosuresRawData): List[MtdError] = run(validationSet, data) - private def parameterRuleValidation: AmendDisclosuresRawData => List[List[MtdError]] = (data: AmendDisclosuresRawData) => { - List( - TaxYearNotSupportedValidation.validate(data.taxYear) - ) - } + private def parameterFormatValidation: ValidationType = (data: AmendDisclosuresRawData) => List( + NinoValidation.validate(data.nino), + TaxYearValidation.validate(data.taxYear) + ) - private def bodyFormatValidator: AmendDisclosuresRawData => List[List[MtdError]] = { data => - List( - JsonFormatValidation.validate[AmendDisclosuresRequestBody](data.body.json) - ) - } + private def parameterRuleValidation: ValidationType = (data: AmendDisclosuresRawData) => List( + TaxYearNotSupportedValidation.validate(data.taxYear) + ) - private def bodyValueValidator: AmendDisclosuresRawData => List[List[MtdError]] = { data => + private def bodyFormatValidator: ValidationType = (data: AmendDisclosuresRawData) => List( + JsonFormatValidation.validate[AmendDisclosuresRequestBody](data.body.json) + ) + private def bodyValueValidator: ValidationType = (data: AmendDisclosuresRawData) => { val requestBodyData = data.body.json.as[AmendDisclosuresRequestBody] List(Validator.flattenErrors( List( - requestBodyData.taxAvoidance.map(_.zipWithIndex.flatMap { - case (data, index) => validateTaxAvoidance(data, index) - }).getOrElse(NoValidationErrors).toList, + requestBodyData.taxAvoidance.map( + _.zipWithIndex.flatMap(item => validateTaxAvoidance(item._1, item._2)) + ).getOrElse(NoValidationErrors).toList, requestBodyData.class2Nics.map(validateClass2Nics).getOrElse(NoValidationErrors) ) )) } - private def validateTaxAvoidance(taxAvoidance: AmendTaxAvoidanceItem, arrayIndex: Int): List[MtdError] = { - List( - SRNValidation.validate(taxAvoidance.srn).map( - _.copy(paths = Some(Seq(s"/taxAvoidance/$arrayIndex/srn"))) - ), - TaxYearValidation.validate(taxAvoidance.taxYear).map( - _.copy(paths = Some(Seq(s"/taxAvoidance/$arrayIndex/taxYear"))) - ) - ).flatten - } + private def validateTaxAvoidance(taxAvoidance: AmendTaxAvoidanceItem, arrayIndex: Int): List[MtdError] = List( + SRNValidation.validate(taxAvoidance.srn).map( + _.copy(paths = Some(List(s"/taxAvoidance/$arrayIndex/srn"))) + ), + TaxYearValidation.validate(taxAvoidance.taxYear).map( + _.copy(paths = Some(List(s"/taxAvoidance/$arrayIndex/taxYear"))) + ) + ).flatten - private def validateClass2Nics(class2Nics: AmendClass2Nics): List[MtdError] = { - List( - VoluntaryClass2ValueValidation.validateOptional(class2Nics.class2VoluntaryContributions).map( - _.copy(paths = Some(Seq("/class2Nics/class2VoluntaryContributions"))) - ) - ).flatten - } -} \ No newline at end of file + private def validateClass2Nics(class2Nics: AmendClass2Nics): List[MtdError] = List( + VoluntaryClass2ValueValidation.validateOptional(class2Nics.class2VoluntaryContributions).map( + _.copy(paths = Some(List("/class2Nics/class2VoluntaryContributions"))) + ) + ).flatten +} diff --git a/app/v1/controllers/requestParsers/validators/CreateMarriageAllowanceValidator.scala b/app/v1/controllers/requestParsers/validators/CreateMarriageAllowanceValidator.scala index d19eb68..6329965 100644 --- a/app/v1/controllers/requestParsers/validators/CreateMarriageAllowanceValidator.scala +++ b/app/v1/controllers/requestParsers/validators/CreateMarriageAllowanceValidator.scala @@ -16,38 +16,35 @@ package v1.controllers.requestParsers.validators -import v1.controllers.requestParsers.validators.validations.{DateFormatValidation, GivenNameValidation, JsonFormatValidation, NinoValidation, SurnameValidation} -import v1.models.errors.{MtdError, PartnerDoBFormatError, PartnerFirstNameFormatError, PartnerNinoFormatError, PartnerSurnameFormatError} +import v1.controllers.requestParsers.validators.validations._ +import v1.models.errors._ import v1.models.request.marriageAllowance.{CreateMarriageAllowanceBody, CreateMarriageAllowanceRawData} + class CreateMarriageAllowanceValidator extends Validator[CreateMarriageAllowanceRawData] { - private val parameterFormatValidation = (data: CreateMarriageAllowanceRawData) => { - List( - NinoValidation.validate(data.nino) - ) - } + private val validationSet = List(parameterFormatValidation, bodyFormatValidation, bodyValueValidation) - private val bodyFormatValidation = (data: CreateMarriageAllowanceRawData) => { - List( - JsonFormatValidation.validate[CreateMarriageAllowanceBody](data.body.json) - ) - } + override def validate(data: CreateMarriageAllowanceRawData): List[MtdError] = run(validationSet, data) - private val bodyValueValidation = (data: CreateMarriageAllowanceRawData) => { - val body = data.body.json.as[CreateMarriageAllowanceBody] + private def parameterFormatValidation: ValidationType = (data: CreateMarriageAllowanceRawData) => List( + NinoValidation.validate(data.nino) + ) - List(Validator.flattenErrors(List( - NinoValidation.validate(body.spouseOrCivilPartnerNino, PartnerNinoFormatError), - SurnameValidation.validate(body.spouseOrCivilPartnerSurname, PartnerSurnameFormatError), - Validator.validateOptional(body.spouseOrCivilPartnerFirstName)(GivenNameValidation.validate(_, PartnerFirstNameFormatError)), - Validator.validateOptional(body.spouseOrCivilPartnerDateOfBirth)(DateFormatValidation.validate(_, PartnerDoBFormatError)), - )) - ) - } - - private val validationSet = List(parameterFormatValidation, bodyFormatValidation, bodyValueValidation) + private def bodyFormatValidation: ValidationType = (data: CreateMarriageAllowanceRawData) => List( + JsonFormatValidation.validate[CreateMarriageAllowanceBody](data.body.json) + ) - override def validate(data: CreateMarriageAllowanceRawData): List[MtdError] = { - run(validationSet, data).distinct + private def bodyValueValidation: ValidationType = (data: CreateMarriageAllowanceRawData) => { + val body = data.body.json.as[CreateMarriageAllowanceBody] + import body._ + + List(Validator.flattenErrors( + List( + NinoValidation.validate(spouseOrCivilPartnerNino, PartnerNinoFormatError), + SurnameValidation.validate(spouseOrCivilPartnerSurname, PartnerSurnameFormatError), + Validator.validateOptional(spouseOrCivilPartnerFirstName)(GivenNameValidation.validate(_, PartnerFirstNameFormatError)), + Validator.validateOptional(spouseOrCivilPartnerDateOfBirth)(DateFormatValidation.validate(_, PartnerDoBFormatError)), + ) + )) } } diff --git a/app/v1/controllers/requestParsers/validators/DeleteRetrieveValidator.scala b/app/v1/controllers/requestParsers/validators/DeleteRetrieveValidator.scala index 683a3e9..3fab140 100644 --- a/app/v1/controllers/requestParsers/validators/DeleteRetrieveValidator.scala +++ b/app/v1/controllers/requestParsers/validators/DeleteRetrieveValidator.scala @@ -17,29 +17,23 @@ package v1.controllers.requestParsers.validators import config.AppConfig -import javax.inject.Inject import v1.controllers.requestParsers.validators.validations._ import v1.models.errors.MtdError import v1.models.request.DeleteRetrieveRawData -class DeleteRetrieveValidator @Inject()(implicit appConfig: AppConfig) extends Validator[DeleteRetrieveRawData] { +import javax.inject.Inject +class DeleteRetrieveValidator @Inject()(implicit appConfig: AppConfig) extends Validator[DeleteRetrieveRawData] { private val validationSet = List(parameterFormatValidation, parameterValueValidation) - override def validate(data: DeleteRetrieveRawData): List[MtdError] = { - run(validationSet, data).distinct - } + override def validate(data: DeleteRetrieveRawData): List[MtdError] = run(validationSet, data) - private def parameterFormatValidation: DeleteRetrieveRawData => List[List[MtdError]] = (data: DeleteRetrieveRawData) => { - List( - NinoValidation.validate(data.nino), - TaxYearValidation.validate(data.taxYear) - ) - } + private def parameterFormatValidation: ValidationType = (data: DeleteRetrieveRawData) => List( + NinoValidation.validate(data.nino), + TaxYearValidation.validate(data.taxYear) + ) - private def parameterValueValidation: DeleteRetrieveRawData => List[List[MtdError]] = (data: DeleteRetrieveRawData) => { - List( - TaxYearNotSupportedValidation.validate(data.taxYear) - ) - } + private def parameterValueValidation: ValidationType = (data: DeleteRetrieveRawData) => List( + TaxYearNotSupportedValidation.validate(data.taxYear) + ) } diff --git a/app/v1/controllers/requestParsers/validators/Validator.scala b/app/v1/controllers/requestParsers/validators/Validator.scala index 227cc01..4753a1e 100644 --- a/app/v1/controllers/requestParsers/validators/Validator.scala +++ b/app/v1/controllers/requestParsers/validators/Validator.scala @@ -19,41 +19,40 @@ package v1.controllers.requestParsers.validators import v1.models.errors.MtdError import v1.models.request.RawData -trait Validator[A <: RawData] { +import scala.annotation.tailrec - type ValidationLevel[T] = T => List[MtdError] +trait Validator[A <: RawData] { + type ValidationType = A => List[List[MtdError]] def validate(data: A): List[MtdError] - def run(validationSet: List[A => List[List[MtdError]]], data: A): List[MtdError] = { + @tailrec + final def run(validationSet: List[ValidationType], data: A): List[MtdError] = { + val nextValidationResultOpt: Option[List[MtdError]] = validationSet.headOption.map(_(data).flatten) - validationSet match { - case Nil => List() - case thisLevel :: remainingLevels => - thisLevel(data).flatten match { - case x if x.isEmpty => run(remainingLevels, data) - case x if x.nonEmpty => x - } + nextValidationResultOpt match { + case None => List.empty[MtdError] + case Some(errs) if errs.nonEmpty => errs + case _ => run(validationSet.tail, data) } } } object Validator { - def flattenErrors(errors: List[List[MtdError]]): List[MtdError] = { - errors.flatten.groupBy(_.message).map { case (_, errors) => + @tailrec + def flattenErrors(errorsToFlatten: List[List[MtdError]], flatErrors: List[MtdError] = List.empty): List[MtdError] = errorsToFlatten.flatten match { + case Nil => flatErrors + case item :: Nil => flatErrors :+ item + case nextError :: tail => + val (matchingErrors, nonMatchingErrors) = tail.partition(_.message == nextError.message) + val nextErrorPaths = matchingErrors.flatMap(_.paths).flatten - val baseError = errors.head.copy(paths = None) + def makeListOptional: List[String] => Option[List[String]] = List => if (List.isEmpty) None else Some(List) - errors.fold(baseError)( - (error1: MtdError, error2: MtdError) => (error1, error2) match { - case (MtdError(_, _, Some(paths1)), MtdError(_, _, Some(paths2))) => error1.copy(paths = Some(paths1 ++ paths2)) - case (MtdError(_, _, Some(_)), MtdError(_, _, None)) => error1 - case (MtdError(_, _, None), MtdError(_, _, Some(_))) => error2 - case _ => error1 - } - ) - }.toList + val newFlatError = nextError.copy(paths = makeListOptional(nextError.paths.getOrElse(List.empty) ++ nextErrorPaths)) + + flattenErrors(List(nonMatchingErrors), flatErrors :+ newFlatError) } /** @@ -65,4 +64,4 @@ object Validator { */ def validateOptional[A](optionalValue: Option[A])(validation: A => List[MtdError]): List[MtdError] = optionalValue.map(a => validation(a)).getOrElse(Nil) -} \ No newline at end of file +} diff --git a/app/v1/controllers/requestParsers/validators/validations/DateFormatValidation.scala b/app/v1/controllers/requestParsers/validators/validations/DateFormatValidation.scala index dec2b2c..d6119f5 100644 --- a/app/v1/controllers/requestParsers/validators/validations/DateFormatValidation.scala +++ b/app/v1/controllers/requestParsers/validators/validations/DateFormatValidation.scala @@ -23,7 +23,6 @@ import java.time.format.DateTimeFormatter import scala.util.{Failure, Success, Try} object DateFormatValidation { - val datePattern = "yyyy-MM-dd" val dateFormat: DateTimeFormatter = DateTimeFormatter.ofPattern(datePattern) @@ -36,4 +35,3 @@ object DateFormatValidation { } } } - diff --git a/app/v1/controllers/requestParsers/validators/validations/JsonFormatValidation.scala b/app/v1/controllers/requestParsers/validators/validations/JsonFormatValidation.scala index daf1709..e34a140 100644 --- a/app/v1/controllers/requestParsers/validators/validations/JsonFormatValidation.scala +++ b/app/v1/controllers/requestParsers/validators/validations/JsonFormatValidation.scala @@ -20,31 +20,32 @@ import play.api.Logger import play.api.libs.json._ import v1.models.errors.{MtdError, RuleIncorrectOrEmptyBodyError} -object JsonFormatValidation { +object JsonFormatValidation { private val logger: Logger = Logger(this.getClass) - def validate[A: OFormat](data: JsValue): List[MtdError] = { - if (data == JsObject.empty) List(RuleIncorrectOrEmptyBodyError) else + def validate[A: OFormat](data: JsValue): List[MtdError] = + if (data == JsObject.empty) List(RuleIncorrectOrEmptyBodyError) else { data.validate[A] match { - case JsSuccess(body, _) => if (Json.toJson(body) == JsObject.empty) List(RuleIncorrectOrEmptyBodyError) else NoValidationErrors - case JsError(errors: Seq[(JsPath, Seq[JsonValidationError])]) => handleErrors(errors) + case JsSuccess(body, _) if Json.toJson(body) == JsObject.empty => List(RuleIncorrectOrEmptyBodyError) + case JsSuccess(_, _) => NoValidationErrors + case jsError: JsError => handleErrors(jsError) } - } + } - private def handleErrors(errors: Seq[(JsPath, Seq[JsonValidationError])]): List[MtdError] = { - val failures = errors.map { + private def handleErrors(jsError: JsError): List[MtdError] = { + val failures = jsError.errors.map { case (path: JsPath, Seq(JsonValidationError(Seq("error.path.missing")))) => MissingMandatoryField(path) case (path: JsPath, Seq(JsonValidationError(Seq(error: String)))) if error.contains("error.expected") => WrongFieldType(path) case (path: JsPath, _) => OtherFailure(path) - } + }.toList val logString = failures.groupBy(_.getClass) .values.map(failure => s"${failure.head.failureReason}: " + s"${failure.map(_.fromJsPath)}") .toString().dropRight(1).drop(5) logger.warn(s"[JsonFormatValidation][validate] - Request body failed validation with errors - $logString") - List(RuleIncorrectOrEmptyBodyError.copy(paths = Some(failures.map(_.fromJsPath)))) + List(RuleIncorrectOrEmptyBodyError.copy(paths = Some(List.from(failures.map(_.fromJsPath))))) } private class JsonFormatValidationFailure(path: JsPath, failure: String) { @@ -57,9 +58,7 @@ object JsonFormatValidation { } private case class MissingMandatoryField(path: JsPath) extends JsonFormatValidationFailure(path, "Missing mandatory field") - private case class WrongFieldType(path: JsPath) extends JsonFormatValidationFailure(path, "Wrong field type") - private case class OtherFailure(path: JsPath) extends JsonFormatValidationFailure(path, "Other failure") } diff --git a/app/v1/controllers/requestParsers/validators/validations/MtdTaxYearValidation.scala b/app/v1/controllers/requestParsers/validators/validations/MtdTaxYearValidation.scala index 7fd4fdb..11cef12 100644 --- a/app/v1/controllers/requestParsers/validators/validations/MtdTaxYearValidation.scala +++ b/app/v1/controllers/requestParsers/validators/validations/MtdTaxYearValidation.scala @@ -17,16 +17,14 @@ package v1.controllers.requestParsers.validators.validations import config.FixedConfig -import v1.models.domain.DesTaxYear +import v1.models.domain.DownstreamTaxYear import v1.models.errors.MtdError object MtdTaxYearValidation extends FixedConfig { // @param taxYear In format YYYY-YY def validate(taxYear: String, error: MtdError): List[MtdError] = { - - val desTaxYear = Integer.parseInt(DesTaxYear.fromMtd(taxYear).value) - - if (desTaxYear >= minimumTaxYear) NoValidationErrors else List(error) + val downstreamTaxYear = Integer.parseInt(DownstreamTaxYear.fromMtd(taxYear).value) + if (downstreamTaxYear >= minimumTaxYear) NoValidationErrors else List(error) } } diff --git a/app/v1/controllers/requestParsers/validators/validations/RegexValidation.scala b/app/v1/controllers/requestParsers/validators/validations/RegexValidation.scala index efe8017..430ff43 100644 --- a/app/v1/controllers/requestParsers/validators/validations/RegexValidation.scala +++ b/app/v1/controllers/requestParsers/validators/validations/RegexValidation.scala @@ -21,12 +21,10 @@ import v1.models.errors.MtdError import scala.util.matching.Regex trait RegexValidation { - val regex: Regex def isValid(value: String): Boolean = { val matcher = regex.pattern.matcher(value) - matcher.matches } diff --git a/app/v1/controllers/requestParsers/validators/validations/SRNValidation.scala b/app/v1/controllers/requestParsers/validators/validations/SRNValidation.scala index 499af40..da35faf 100644 --- a/app/v1/controllers/requestParsers/validators/validations/SRNValidation.scala +++ b/app/v1/controllers/requestParsers/validators/validations/SRNValidation.scala @@ -19,10 +19,9 @@ package v1.controllers.requestParsers.validators.validations import v1.models.errors.{MtdError, SRNFormatError} object SRNValidation { - private val regex = "^[0-9]{8}$" def validate(srn: String): List[MtdError] = { - if(srn.matches(regex)) NoValidationErrors else List(SRNFormatError) + if (srn.matches(regex)) NoValidationErrors else List(SRNFormatError) } -} \ No newline at end of file +} diff --git a/app/v1/controllers/requestParsers/validators/validations/SurnameValidation.scala b/app/v1/controllers/requestParsers/validators/validations/SurnameValidation.scala index 29a60b4..095c2af 100644 --- a/app/v1/controllers/requestParsers/validators/validations/SurnameValidation.scala +++ b/app/v1/controllers/requestParsers/validators/validations/SurnameValidation.scala @@ -15,6 +15,7 @@ */ package v1.controllers.requestParsers.validators.validations + import scala.util.matching.Regex object SurnameValidation extends RegexValidation { diff --git a/app/v1/controllers/requestParsers/validators/validations/TaxYearNotSupportedValidation.scala b/app/v1/controllers/requestParsers/validators/validations/TaxYearNotSupportedValidation.scala index e479184..a4d9530 100644 --- a/app/v1/controllers/requestParsers/validators/validations/TaxYearNotSupportedValidation.scala +++ b/app/v1/controllers/requestParsers/validators/validations/TaxYearNotSupportedValidation.scala @@ -17,16 +17,20 @@ package v1.controllers.requestParsers.validators.validations import config.AppConfig -import v1.models.domain.DesTaxYear +import v1.models.domain.DownstreamTaxYear import v1.models.errors.{MtdError, RuleTaxYearNotSupportedError} object TaxYearNotSupportedValidation { // @param taxYear In format YYYY-YY def validate(taxYear: String)(implicit appConfig: AppConfig): List[MtdError] = { - val desTaxYear = Integer.parseInt(DesTaxYear.fromMtd(taxYear).value) + val downstreamTaxYear = Integer.parseInt(DownstreamTaxYear.fromMtd(taxYear).value) - if (desTaxYear < appConfig.minimumPermittedTaxYear) List(RuleTaxYearNotSupportedError) - else NoValidationErrors + if (downstreamTaxYear < appConfig.minimumPermittedTaxYear) { + List(RuleTaxYearNotSupportedError) + } + else { + NoValidationErrors + } } } diff --git a/app/v1/controllers/requestParsers/validators/validations/TaxYearValidation.scala b/app/v1/controllers/requestParsers/validators/validations/TaxYearValidation.scala index f992fce..0003947 100644 --- a/app/v1/controllers/requestParsers/validators/validations/TaxYearValidation.scala +++ b/app/v1/controllers/requestParsers/validators/validations/TaxYearValidation.scala @@ -19,14 +19,12 @@ package v1.controllers.requestParsers.validators.validations import v1.models.errors.{MtdError, RuleTaxYearRangeInvalidError, TaxYearFormatError} object TaxYearValidation { - val taxYearFormat = "20[1-9][0-9]\\-[1-9][0-9]" def validate(taxYear: String): List[MtdError] = { if (taxYear.matches(taxYearFormat)) { - val start = taxYear.substring(2, 4).toInt - val end = taxYear.substring(5, 7).toInt + val end = taxYear.substring(5, 7).toInt if (end - start == 1) { NoValidationErrors @@ -37,5 +35,4 @@ object TaxYearValidation { List(TaxYearFormatError) } } - -} \ No newline at end of file +} diff --git a/app/v1/controllers/requestParsers/validators/validations/VoluntaryClass2ValueValidation.scala b/app/v1/controllers/requestParsers/validators/validations/VoluntaryClass2ValueValidation.scala index a0c2333..70c6652 100644 --- a/app/v1/controllers/requestParsers/validators/validations/VoluntaryClass2ValueValidation.scala +++ b/app/v1/controllers/requestParsers/validators/validations/VoluntaryClass2ValueValidation.scala @@ -22,6 +22,6 @@ object VoluntaryClass2ValueValidation { def validateOptional(class2VoluntaryContributions: Option[Boolean]): List[MtdError] = class2VoluntaryContributions.fold(NoValidationErrors: List[MtdError]) { value => - if(value) NoValidationErrors else List(RuleVoluntaryClass2ValueInvalidError) - } + if (value) NoValidationErrors else List(RuleVoluntaryClass2ValueInvalidError) + } } \ No newline at end of file diff --git a/app/v1/controllers/requestParsers/validators/validations/package.scala b/app/v1/controllers/requestParsers/validators/validations/package.scala index 6f17109..65d460e 100644 --- a/app/v1/controllers/requestParsers/validators/validations/package.scala +++ b/app/v1/controllers/requestParsers/validators/validations/package.scala @@ -16,8 +16,8 @@ package v1.controllers.requestParsers.validators -package object validations { - - val NoValidationErrors = List() +import v1.models.errors.MtdError +package object validations { + val NoValidationErrors = List.empty[MtdError] } diff --git a/app/v1/hateoas/HateoasFactory.scala b/app/v1/hateoas/HateoasFactory.scala index 271ede9..2f59628 100644 --- a/app/v1/hateoas/HateoasFactory.scala +++ b/app/v1/hateoas/HateoasFactory.scala @@ -22,8 +22,6 @@ import config.AppConfig import javax.inject.Inject import v1.models.hateoas._ -import scala.language.higherKinds - class HateoasFactory @Inject()(appConfig: AppConfig) { def wrap[A, D <: HateoasData](payload: A, data: D)(implicit lf: HateoasLinksFactory[A, D]): HateoasWrapper[A] = { diff --git a/app/v1/models/audit/AuditEvent.scala b/app/v1/models/audit/AuditEvent.scala index c1b34fb..b9351b8 100644 --- a/app/v1/models/audit/AuditEvent.scala +++ b/app/v1/models/audit/AuditEvent.scala @@ -16,8 +16,6 @@ package v1.models.audit -case class AuditEvent[T]( - auditType: String, - transactionName: String, - detail: T - ) +case class AuditEvent[T](auditType: String, + transactionName: String, + detail: T) diff --git a/app/v1/models/audit/AuditResponse.scala b/app/v1/models/audit/AuditResponse.scala index b93625d..88612ed 100644 --- a/app/v1/models/audit/AuditResponse.scala +++ b/app/v1/models/audit/AuditResponse.scala @@ -18,14 +18,15 @@ package v1.models.audit import play.api.libs.json.{JsValue, Json, OWrites} -case class AuditResponse(httpStatus: Int, errors: Option[Seq[AuditError]], body: Option[JsValue]) + +case class AuditResponse(httpStatus: Int, errors: Option[List[AuditError]], body: Option[JsValue]) object AuditResponse { implicit val writes: OWrites[AuditResponse] = Json.writes[AuditResponse] - def apply(httpStatus: Int, response: Either[Seq[AuditError], Option[JsValue]]): AuditResponse = + def apply(httpStatus: Int, response: Either[List[AuditError], Option[JsValue]]): AuditResponse = response match { case Right(body) => AuditResponse(httpStatus, None, body) - case Left(errs) => AuditResponse(httpStatus, Some(errs), None) + case Left(errs) => AuditResponse(httpStatus, Some(errs), None) } } diff --git a/app/v1/models/domain/DesTaxYear.scala b/app/v1/models/domain/DownstreamTaxYear.scala similarity index 75% rename from app/v1/models/domain/DesTaxYear.scala rename to app/v1/models/domain/DownstreamTaxYear.scala index f879dc1..5c97957 100644 --- a/app/v1/models/domain/DesTaxYear.scala +++ b/app/v1/models/domain/DownstreamTaxYear.scala @@ -17,19 +17,18 @@ package v1.models.domain /** - * Represents a tax year for DES + * Represents a tax year in the format required by downstream services * * @param value the tax year string (where 2018 represents 2017-18) */ -case class DesTaxYear(value: String) extends AnyVal { +case class DownstreamTaxYear(value: String) extends AnyVal { override def toString: String = value } -object DesTaxYear { +object DownstreamTaxYear { /** * @param taxYear tax year in MTD format (e.g. 2017-18) */ - def fromMtd(taxYear: String): DesTaxYear = - DesTaxYear(taxYear.take(2) + taxYear.drop(5)) + def fromMtd(taxYear: String): DownstreamTaxYear = DownstreamTaxYear(taxYear.take(2) + taxYear.drop(5)) } diff --git a/app/v1/models/domain/Nino.scala b/app/v1/models/domain/Nino.scala index 9521075..cddc332 100644 --- a/app/v1/models/domain/Nino.scala +++ b/app/v1/models/domain/Nino.scala @@ -38,4 +38,3 @@ object Nino extends (String => Nino) { val validPrefixes: Seq[String] = validFirstCharacters.flatMap(a => validSecondCharacters.map(a + _)).filterNot(invalidPrefixes.contains(_)) val validSuffixes: Seq[String] = ('A' to 'D').map(_.toString) } - diff --git a/app/v1/models/errors/ErrorWrapper.scala b/app/v1/models/errors/ErrorWrapper.scala index 6417d50..29309f7 100644 --- a/app/v1/models/errors/ErrorWrapper.scala +++ b/app/v1/models/errors/ErrorWrapper.scala @@ -19,20 +19,20 @@ package v1.models.errors import play.api.libs.json.{JsObject, Json, Writes} import v1.models.audit.AuditError -case class ErrorWrapper(correlationId: String, error: MtdError, errors: Option[Seq[MtdError]] = None) { - private def allErrors: Seq[MtdError] = errors match { - case Some(seq) => seq - case None => Seq(error) +case class ErrorWrapper(correlationId: String, error: MtdError, errors: Option[List[MtdError]] = None) { + + private def allErrors: List[MtdError] = errors match { + case Some(list) => list + case None => List(error) } - def auditErrors: Seq[AuditError] = + def auditErrors: List[AuditError] = allErrors.map(error => AuditError(error.code)) } object ErrorWrapper { implicit val writes: Writes[ErrorWrapper] = (errorResponse: ErrorWrapper) => { - val json = Json.toJson(errorResponse.error).as[JsObject] errorResponse.errors match { @@ -40,4 +40,4 @@ object ErrorWrapper { case _ => json } } -} \ No newline at end of file +} diff --git a/app/v1/models/errors/desErrors.scala b/app/v1/models/errors/downstreamErrors.scala similarity index 59% rename from app/v1/models/errors/desErrors.scala rename to app/v1/models/errors/downstreamErrors.scala index 4ca75e9..ba5adb4 100644 --- a/app/v1/models/errors/desErrors.scala +++ b/app/v1/models/errors/downstreamErrors.scala @@ -18,20 +18,21 @@ package v1.models.errors import play.api.libs.json.{Json, Reads} -case class DesErrorCode(code: String) { + +case class DownstreamErrorCode(code: String) { def toMtd: MtdError = MtdError(code = code, message = "") } -object DesErrorCode { - implicit val reads: Reads[DesErrorCode] = Json.reads[DesErrorCode] +object DownstreamErrorCode { + implicit val reads: Reads[DownstreamErrorCode] = Json.reads[DownstreamErrorCode] } -sealed trait DesError +sealed trait DownstreamError -case class DesErrors(errors: List[DesErrorCode]) extends DesError +case class DownstreamErrors(errors: List[DownstreamErrorCode]) extends DownstreamError -object DesErrors { - def single(error: DesErrorCode): DesErrors = DesErrors(List(error)) +object DownstreamErrors { + def single(error: DownstreamErrorCode): DownstreamErrors = DownstreamErrors(List(error)) } -case class OutboundError(error: MtdError, errors: Option[Seq[MtdError]] = None) extends DesError +case class OutboundError(error: MtdError, errors: Option[List[MtdError]] = None) extends DownstreamError diff --git a/app/v1/models/errors/mtdErrors.scala b/app/v1/models/errors/mtdErrors.scala index c106800..bfa7ac4 100644 --- a/app/v1/models/errors/mtdErrors.scala +++ b/app/v1/models/errors/mtdErrors.scala @@ -18,7 +18,8 @@ package v1.models.errors import play.api.libs.json.{Json, OWrites} -case class MtdError(code: String, message: String, paths: Option[Seq[String]] = None) + +case class MtdError(code: String, message: String, paths: Option[List[String]] = None) object MtdError { implicit val writes: OWrites[MtdError] = Json.writes[MtdError] @@ -114,7 +115,7 @@ object NotFoundError extends MtdError( message = "Matching resource not found" ) -object DownstreamError extends MtdError( +object InternalError extends MtdError( code = "INTERNAL_SERVER_ERROR", message = "An internal server error occurred" ) diff --git a/app/v1/models/outcomes/package.scala b/app/v1/models/outcomes/package.scala index 5bde7b0..1d023f5 100644 --- a/app/v1/models/outcomes/package.scala +++ b/app/v1/models/outcomes/package.scala @@ -20,7 +20,5 @@ import v1.models.auth.UserDetails import v1.models.errors.MtdError package object outcomes { - type AuthOutcome = Either[MtdError, UserDetails] - } diff --git a/app/v1/services/AmendDisclosuresService.scala b/app/v1/services/AmendDisclosuresService.scala index 37295d7..6ad29d7 100644 --- a/app/v1/services/AmendDisclosuresService.scala +++ b/app/v1/services/AmendDisclosuresService.scala @@ -26,12 +26,12 @@ import v1.controllers.EndpointLogContext import v1.models.errors._ import v1.models.outcomes.ResponseWrapper import v1.models.request.disclosures.AmendDisclosuresRequest -import v1.support.DesResponseMappingSupport +import v1.support.DownstreamResponseMappingSupport import scala.concurrent.{ExecutionContext, Future} @Singleton -class AmendDisclosuresService @Inject()(connector: AmendDisclosuresConnector) extends DesResponseMappingSupport with Logging { +class AmendDisclosuresService @Inject()(connector: AmendDisclosuresConnector) extends DownstreamResponseMappingSupport with Logging { def amendDisclosures(request: AmendDisclosuresRequest) (implicit hc: HeaderCarrier,ec: ExecutionContext, @@ -39,8 +39,8 @@ class AmendDisclosuresService @Inject()(connector: AmendDisclosuresConnector) ex correlationId: String): Future[Either[ErrorWrapper, ResponseWrapper[Unit]]] = { val result = for { - desResponseWrapper <- EitherT(connector.amendDisclosures(request)).leftMap(mapDesErrors(ifsErrorMap)) - } yield desResponseWrapper + downstreamResponseWrapper <- EitherT(connector.amendDisclosures(request)).leftMap(mapDownstreamErrors(ifsErrorMap)) + } yield downstreamResponseWrapper result.value } @@ -49,11 +49,11 @@ class AmendDisclosuresService @Inject()(connector: AmendDisclosuresConnector) ex Map( "INVALID_TAXABLE_ENTITY_ID" -> NinoFormatError, "INVALID_TAX_YEAR" -> TaxYearFormatError, - "INVALID_CORRELATIONID" -> DownstreamError, - "INVALID_PAYLOAD" -> DownstreamError, + "INVALID_CORRELATIONID" -> InternalError, + "INVALID_PAYLOAD" -> InternalError, "INCOME_SOURCE_NOT_FOUND" -> NotFoundError, "VOLUNTARY_CLASS2_CANNOT_BE_CHANGED" -> RuleVoluntaryClass2CannotBeChangedError, - "SERVER_ERROR" -> DownstreamError, - "SERVICE_UNAVAILABLE" -> DownstreamError + "SERVER_ERROR" -> InternalError, + "SERVICE_UNAVAILABLE" -> InternalError ) } \ No newline at end of file diff --git a/app/v1/services/CreateMarriageAllowanceService.scala b/app/v1/services/CreateMarriageAllowanceService.scala index 557c040..09c00f3 100644 --- a/app/v1/services/CreateMarriageAllowanceService.scala +++ b/app/v1/services/CreateMarriageAllowanceService.scala @@ -26,12 +26,12 @@ import v1.controllers.EndpointLogContext import v1.models.errors._ import v1.models.outcomes.ResponseWrapper import v1.models.request.marriageAllowance.CreateMarriageAllowanceRequest -import v1.support.DesResponseMappingSupport +import v1.support.DownstreamResponseMappingSupport import scala.concurrent.{ExecutionContext, Future} @Singleton -class CreateMarriageAllowanceService @Inject()(connector: CreateMarriageAllowanceConnector) extends DesResponseMappingSupport with Logging { +class CreateMarriageAllowanceService @Inject()(connector: CreateMarriageAllowanceConnector) extends DownstreamResponseMappingSupport with Logging { def create(request: CreateMarriageAllowanceRequest) (implicit hc: HeaderCarrier,ec: ExecutionContext, @@ -39,8 +39,8 @@ class CreateMarriageAllowanceService @Inject()(connector: CreateMarriageAllowanc correlationId: String): Future[Either[ErrorWrapper, ResponseWrapper[Unit]]] = { val result = for { - desResponseWrapper <- EitherT(connector.create(request)).leftMap(mapDesErrors(ifsErrorMap)) - } yield desResponseWrapper + downstreamResponseWrapper <- EitherT(connector.create(request)).leftMap(mapDownstreamErrors(ifsErrorMap)) + } yield downstreamResponseWrapper result.value } @@ -50,21 +50,21 @@ class CreateMarriageAllowanceService @Inject()(connector: CreateMarriageAllowanc "INVALID_IDVALUE" -> NinoFormatError, "DECEASED_PARTICIPANT" -> RuleDeceasedRecipientError, "RELATIONSHIP_ALREADY_EXISTS" -> RuleActiveMarriageAllowanceClaimError, - "INVALID_IDTYPE" -> DownstreamError, - "END_DATE_CODE_NOT_FOUND" -> DownstreamError, - "INVALID_CORRELATIONID" -> DownstreamError, - "INVALID_PAYLOAD" -> DownstreamError, + "INVALID_IDTYPE" -> InternalError, + "END_DATE_CODE_NOT_FOUND" -> InternalError, + "INVALID_CORRELATIONID" -> InternalError, + "INVALID_PAYLOAD" -> InternalError, "NINO_OR_TRN_NOT_FOUND" -> RuleInvalidRequestError, - "INVALID_ACTUAL_END_DATE" -> DownstreamError, - "INVALID_PARTICIPANT_END_DATE" -> DownstreamError, - "INVALID_PARTICIPANT_START_DATE" -> DownstreamError, - "INVALID_RELATIONSHIP_CODE" -> DownstreamError, - "PARTICIPANT1_CANNOT_BE_UPDATED" -> DownstreamError, - "PARTICIPANT2_CANNOT_BE_UPDATED" -> DownstreamError, - "CONFIDENCE_CHECK_FAILED" -> DownstreamError, - "CONFIDENCE_CHECK_SURNAME_MISSED" -> DownstreamError, - "BAD_GATEWAY" -> DownstreamError, - "SERVER_ERROR" -> DownstreamError, - "SERVICE_UNAVAILABLE" -> DownstreamError + "INVALID_ACTUAL_END_DATE" -> InternalError, + "INVALID_PARTICIPANT_END_DATE" -> InternalError, + "INVALID_PARTICIPANT_START_DATE" -> InternalError, + "INVALID_RELATIONSHIP_CODE" -> InternalError, + "PARTICIPANT1_CANNOT_BE_UPDATED" -> InternalError, + "PARTICIPANT2_CANNOT_BE_UPDATED" -> InternalError, + "CONFIDENCE_CHECK_FAILED" -> InternalError, + "CONFIDENCE_CHECK_SURNAME_MISSED" -> InternalError, + "BAD_GATEWAY" -> InternalError, + "SERVER_ERROR" -> InternalError, + "SERVICE_UNAVAILABLE" -> InternalError ) } diff --git a/app/v1/services/DeleteRetrieveService.scala b/app/v1/services/DeleteRetrieveService.scala index d633151..ef9b939 100644 --- a/app/v1/services/DeleteRetrieveService.scala +++ b/app/v1/services/DeleteRetrieveService.scala @@ -27,47 +27,47 @@ import v1.connectors.DownstreamUri.Ifs1Uri import v1.controllers.EndpointLogContext import v1.models.errors._ import v1.models.outcomes.ResponseWrapper -import v1.support.DesResponseMappingSupport +import v1.support.DownstreamResponseMappingSupport import scala.concurrent.{ExecutionContext, Future} @Singleton -class DeleteRetrieveService @Inject()(connector: DeleteRetrieveConnector) extends DesResponseMappingSupport with Logging { +class DeleteRetrieveService @Inject()(connector: DeleteRetrieveConnector) extends DownstreamResponseMappingSupport with Logging { - def delete(desErrorMap: Map[String, MtdError] = defaultDesErrorMap)(implicit hc: HeaderCarrier, + def delete(downstreamErrorMap: Map[String, MtdError] = defaultDownstreamErrorMap)(implicit hc: HeaderCarrier, ec: ExecutionContext, logContext: EndpointLogContext, ifs1Uri: Ifs1Uri[Unit], correlationId: String): Future[Either[ErrorWrapper, ResponseWrapper[Unit]]] = { val result = for { - desResponseWrapper <- EitherT(connector.delete()).leftMap(mapDesErrors(desErrorMap)) - } yield desResponseWrapper + downstreamResponseWrapper <- EitherT(connector.delete()).leftMap(mapDownstreamErrors(downstreamErrorMap)) + } yield downstreamResponseWrapper result.value } - def retrieve[Resp: Format](desErrorMap: Map[String, MtdError] = defaultDesErrorMap)(implicit hc: HeaderCarrier, + def retrieve[Resp: Format](downstreamErrorMap: Map[String, MtdError] = defaultDownstreamErrorMap)(implicit hc: HeaderCarrier, ec: ExecutionContext, logContext: EndpointLogContext, ifs1Uri: Ifs1Uri[Resp], correlationId: String): Future[Either[ErrorWrapper, ResponseWrapper[Resp]]] = { val result = for { - desResponseWrapper <- EitherT(connector.retrieve[Resp]()).leftMap(mapDesErrors(desErrorMap)) - mtdResponseWrapper <- EitherT.fromEither[Future](validateRetrieveResponse(desResponseWrapper)) + downstreamResponseWrapper <- EitherT(connector.retrieve[Resp]()).leftMap(mapDownstreamErrors(downstreamErrorMap)) + mtdResponseWrapper <- EitherT.fromEither[Future](validateRetrieveResponse(downstreamResponseWrapper)) } yield mtdResponseWrapper result.value } - private def defaultDesErrorMap: Map[String, MtdError] = + private def defaultDownstreamErrorMap: Map[String, MtdError] = Map( "INVALID_TAXABLE_ENTITY_ID" -> NinoFormatError, "INVALID_TAX_YEAR" -> TaxYearFormatError, - "INVALID_CORRELATIONID" -> DownstreamError, + "INVALID_CORRELATIONID" -> InternalError, "NO_DATA_FOUND" -> NotFoundError, - "SERVER_ERROR" -> DownstreamError, - "SERVICE_UNAVAILABLE" -> DownstreamError + "SERVER_ERROR" -> InternalError, + "SERVICE_UNAVAILABLE" -> InternalError ) } \ No newline at end of file diff --git a/app/v1/services/EnrolmentsAuthService.scala b/app/v1/services/EnrolmentsAuthService.scala index 405b30a..7447f9a 100644 --- a/app/v1/services/EnrolmentsAuthService.scala +++ b/app/v1/services/EnrolmentsAuthService.scala @@ -26,7 +26,7 @@ import uk.gov.hmrc.auth.core.retrieve.v2.Retrievals._ import uk.gov.hmrc.auth.core.retrieve.~ import uk.gov.hmrc.http.HeaderCarrier import v1.models.auth.UserDetails -import v1.models.errors.{DownstreamError, UnauthorisedError} +import v1.models.errors.{InternalError, UnauthorisedError} import v1.models.outcomes.AuthOutcome import javax.inject.{Inject, Singleton} @@ -68,7 +68,7 @@ class EnrolmentsAuthService @Inject()(val connector: AuthConnector, val appConfi user case None => logger.warn(s"[EnrolmentsAuthService][authorised] No AgentReferenceNumber defined on agent enrolment.") - Left(DownstreamError) + Left(InternalError) } case _ ~ _ => logger.warn(s"[EnrolmentsAuthService][authorised] Invalid AffinityGroup.") @@ -78,7 +78,7 @@ class EnrolmentsAuthService @Inject()(val connector: AuthConnector, val appConfi case _: AuthorisationException => Future.successful(Left(UnauthorisedError)) case error => logger.warn(s"[EnrolmentsAuthService][authorised] An unexpected error occurred: $error") - Future.successful(Left(DownstreamError)) + Future.successful(Left(InternalError)) } } diff --git a/app/v1/support/DesResponseMappingSupport.scala b/app/v1/support/DownstreamResponseMappingSupport.scala similarity index 64% rename from app/v1/support/DesResponseMappingSupport.scala rename to app/v1/support/DownstreamResponseMappingSupport.scala index 085ddcb..18bb817 100644 --- a/app/v1/support/DesResponseMappingSupport.scala +++ b/app/v1/support/DownstreamResponseMappingSupport.scala @@ -22,39 +22,40 @@ import v1.controllers.EndpointLogContext import v1.models.errors._ import v1.models.outcomes.ResponseWrapper -trait DesResponseMappingSupport { +trait DownstreamResponseMappingSupport { self: Logging => - final def validateRetrieveResponse[T: Writes](desResponseWrapper: ResponseWrapper[T]): Either[ErrorWrapper, ResponseWrapper[T]] = { - if (Json.toJson(desResponseWrapper.responseData) == JsObject.empty) { - Left(ErrorWrapper(desResponseWrapper.correlationId, NotFoundError, None)) + final def validateRetrieveResponse[T: Writes](downstreamResponseWrapper: ResponseWrapper[T]): Either[ErrorWrapper, ResponseWrapper[T]] = { + if (Json.toJson(downstreamResponseWrapper.responseData) == JsObject.empty) { + Left(ErrorWrapper(downstreamResponseWrapper.correlationId, NotFoundError, None)) } else { - Right(desResponseWrapper) + Right(downstreamResponseWrapper) } } - final def mapDesErrors[D](errorCodeMap: PartialFunction[String, MtdError])(desResponseWrapper: ResponseWrapper[DesError])( + final def mapDownstreamErrors[D](errorCodeMap: PartialFunction[String, MtdError])(downstreamResponseWrapper: ResponseWrapper[DownstreamError])( implicit logContext: EndpointLogContext): ErrorWrapper = { lazy val defaultErrorCodeMapping: String => MtdError = { code => logger.warn(s"[${logContext.controllerName}] [${logContext.endpointName}] - No mapping found for error code $code") - DownstreamError + InternalError } - desResponseWrapper match { - case ResponseWrapper(correlationId, DesErrors(error :: Nil)) => + downstreamResponseWrapper match { + case ResponseWrapper(correlationId, DownstreamErrors(error :: Nil)) => ErrorWrapper(correlationId, errorCodeMap.applyOrElse(error.code, defaultErrorCodeMapping), None) - case ResponseWrapper(correlationId, DesErrors(errorCodes)) => + case ResponseWrapper(correlationId, DownstreamErrors(errorCodes)) => + val mtdErrors = errorCodes.map(error => errorCodeMap.applyOrElse(error.code, defaultErrorCodeMapping)) - if (mtdErrors.contains(DownstreamError)) { + if (mtdErrors.contains(InternalError)) { logger.warn( s"[${logContext.controllerName}] [${logContext.endpointName}] [CorrelationId - $correlationId]" + s" - downstream returned ${errorCodes.map(_.code).mkString(",")}. Revert to ISE") - ErrorWrapper(correlationId, DownstreamError, None) + ErrorWrapper(correlationId, InternalError, None) } else { - ErrorWrapper(correlationId, BadRequestError, Some(mtdErrors)) + ErrorWrapper(correlationId, BadRequestError, Some(List.from(mtdErrors))) } case ResponseWrapper(correlationId, OutboundError(error, errors)) => diff --git a/build.sbt b/build.sbt index 2f89f6b..5e2201b 100644 --- a/build.sbt +++ b/build.sbt @@ -32,7 +32,7 @@ lazy val microservice = Project(appName, file(".")) libraryDependencies ++= AppDependencies.compile ++ AppDependencies.test(), retrieveManaged := true, update / evictionWarningOptions := EvictionWarningOptions.default.withWarnScalaVersionEviction(warnScalaVersionEviction = false), - scalaVersion := "2.12.15", + scalaVersion := "2.13.8", scalacOptions ++= Seq("-Xfatal-warnings", "-Wconf:src=routes/.*:silent", "-feature", "-language:higherKinds") ) .settings( diff --git a/conf/application.conf b/conf/application.conf index f222bd9..0392e9d 100644 --- a/conf/application.conf +++ b/conf/application.conf @@ -21,7 +21,7 @@ appName = individuals-disclosures-api appUrl = "http://localhost:7799" -minimumPermittedTaxYear = 2020 # This is in DES format so '2020' corrosponds to the 2019-20 tax year. +minimumPermittedTaxYear = 2020 # This is in DOWNSTREAM format so '2020' corrosponds to the 2019-20 tax year. # Session Timeout # ~~~~ diff --git a/it/v1/endpoints/AmendDisclosuresControllerISpec.scala b/it/v1/endpoints/AmendDisclosuresControllerISpec.scala index c4e44ba..c512b5c 100644 --- a/it/v1/endpoints/AmendDisclosuresControllerISpec.scala +++ b/it/v1/endpoints/AmendDisclosuresControllerISpec.scala @@ -24,7 +24,8 @@ import play.api.libs.ws.{WSRequest, WSResponse} import play.api.test.Helpers.AUTHORIZATION import support.IntegrationBaseSpec import v1.models.errors._ -import v1.stubs.{AuditStub, AuthStub, DesStub, MtdIdLookupStub} +import v1.stubs.{AuditStub, AuthStub, DownstreamStub, MtdIdLookupStub} + class AmendDisclosuresControllerISpec extends IntegrationBaseSpec { @@ -102,7 +103,7 @@ class AmendDisclosuresControllerISpec extends IntegrationBaseSpec { AuditStub.audit() AuthStub.authorised() MtdIdLookupStub.ninoFound(nino) - DesStub.onSuccess(DesStub.PUT, ifs1Uri, NO_CONTENT) + DownstreamStub.onSuccess(DownstreamStub.PUT, ifs1Uri, NO_CONTENT) } val response: WSResponse = await(request().put(requestBodyJson)) @@ -135,7 +136,7 @@ class AmendDisclosuresControllerISpec extends IntegrationBaseSpec { AuditStub.audit() AuthStub.authorised() MtdIdLookupStub.ninoFound(nino) - DesStub.onSuccess(DesStub.PUT, ifs1Uri, NO_CONTENT) + DownstreamStub.onSuccess(DownstreamStub.PUT, ifs1Uri, NO_CONTENT) } val response: WSResponse = await(request().put(invalidTaxYearRequestBodyJson)) @@ -174,7 +175,7 @@ class AmendDisclosuresControllerISpec extends IntegrationBaseSpec { AuditStub.audit() AuthStub.authorised() MtdIdLookupStub.ninoFound(nino) - DesStub.onSuccess(DesStub.PUT, ifs1Uri, NO_CONTENT) + DownstreamStub.onSuccess(DownstreamStub.PUT, ifs1Uri, NO_CONTENT) } val response: WSResponse = await(request().put(invalidTaxYearRequestBodyJson)) @@ -397,7 +398,7 @@ class AmendDisclosuresControllerISpec extends IntegrationBaseSpec { ) val incorrectBodyError: MtdError = RuleIncorrectOrEmptyBodyError.copy( - paths = Some(Seq("/taxAvoidance/0/srn")) + paths = Some(List("/taxAvoidance/0/srn")) ) val invalidSRNRequestBodyJson: JsValue = Json.parse( @@ -417,7 +418,7 @@ class AmendDisclosuresControllerISpec extends IntegrationBaseSpec { ) val srnFormatError: MtdError = SRNFormatError.copy( - paths = Some(Seq("/taxAvoidance/0/srn")) + paths = Some(List("/taxAvoidance/0/srn")) ) val invalidClass2ValueRequestBodyJson: JsValue = Json.parse( @@ -437,7 +438,7 @@ class AmendDisclosuresControllerISpec extends IntegrationBaseSpec { ) val ruleVoluntaryClass2ValueInvalidError: MtdError = RuleVoluntaryClass2ValueInvalidError.copy( - paths = Some(Seq("/class2Nics/class2VoluntaryContributions")) + paths = Some(List("/class2Nics/class2VoluntaryContributions")) ) "validation error" when { @@ -483,7 +484,7 @@ class AmendDisclosuresControllerISpec extends IntegrationBaseSpec { AuditStub.audit() AuthStub.authorised() MtdIdLookupStub.ninoFound(nino) - DesStub.onError(DesStub.PUT, ifs1Uri, ifsStatus, errorBody(ifsCode)) + DownstreamStub.onError(DownstreamStub.PUT, ifs1Uri, ifsStatus, errorBody(ifsCode)) } val response: WSResponse = await(request().put(requestBodyJson)) @@ -503,12 +504,12 @@ class AmendDisclosuresControllerISpec extends IntegrationBaseSpec { val input = Seq( (BAD_REQUEST, "INVALID_TAXABLE_ENTITY_ID", BAD_REQUEST, NinoFormatError), (BAD_REQUEST, "INVALID_TAX_YEAR", BAD_REQUEST, TaxYearFormatError), - (BAD_REQUEST, "INVALID_CORRELATIONID", INTERNAL_SERVER_ERROR, DownstreamError), - (BAD_REQUEST, "INVALID_PAYLOAD", INTERNAL_SERVER_ERROR, DownstreamError), + (BAD_REQUEST, "INVALID_CORRELATIONID", INTERNAL_SERVER_ERROR, InternalError), + (BAD_REQUEST, "INVALID_PAYLOAD", INTERNAL_SERVER_ERROR, InternalError), (NOT_FOUND, "INCOME_SOURCE_NOT_FOUND", NOT_FOUND, NotFoundError), (UNPROCESSABLE_ENTITY, "VOLUNTARY_CLASS2_CANNOT_BE_CHANGED", FORBIDDEN, RuleVoluntaryClass2CannotBeChangedError), - (SERVICE_UNAVAILABLE, "SERVICE_UNAVAILABLE", INTERNAL_SERVER_ERROR, DownstreamError), - (INTERNAL_SERVER_ERROR, "SERVER_ERROR", INTERNAL_SERVER_ERROR, DownstreamError) + (SERVICE_UNAVAILABLE, "SERVICE_UNAVAILABLE", INTERNAL_SERVER_ERROR, InternalError), + (INTERNAL_SERVER_ERROR, "SERVER_ERROR", INTERNAL_SERVER_ERROR, InternalError) ) input.foreach(args => (serviceErrorTest _).tupled(args)) } diff --git a/it/v1/endpoints/AuthISpec.scala b/it/v1/endpoints/AuthISpec.scala index 60b48dc..72c5b80 100644 --- a/it/v1/endpoints/AuthISpec.scala +++ b/it/v1/endpoints/AuthISpec.scala @@ -23,7 +23,7 @@ import play.api.http.Status.NO_CONTENT import play.api.libs.ws.{WSRequest, WSResponse} import play.api.test.Helpers.AUTHORIZATION import support.IntegrationBaseSpec -import v1.stubs.{AuditStub, AuthStub, DesStub, MtdIdLookupStub} +import v1.stubs.{AuditStub, AuthStub, DownstreamStub, MtdIdLookupStub} class AuthISpec extends IntegrationBaseSpec { @@ -61,7 +61,7 @@ class AuthISpec extends IntegrationBaseSpec { MtdIdLookupStub.internalServerError(nino) } - val response: WSResponse = await(request().delete) + val response: WSResponse = await(request().delete()) response.status shouldBe Status.INTERNAL_SERVER_ERROR } } @@ -73,10 +73,10 @@ class AuthISpec extends IntegrationBaseSpec { AuditStub.audit() AuthStub.authorised() MtdIdLookupStub.ninoFound(nino) - DesStub.onSuccess(DesStub.DELETE, ifs1Uri, NO_CONTENT) + DownstreamStub.onSuccess(DownstreamStub.DELETE, ifs1Uri, NO_CONTENT) } - val response: WSResponse = await(request().delete) + val response: WSResponse = await(request().delete()) response.status shouldBe Status.NO_CONTENT } } @@ -92,7 +92,7 @@ class AuthISpec extends IntegrationBaseSpec { AuthStub.unauthorisedNotLoggedIn() } - val response: WSResponse = await(request().delete) + val response: WSResponse = await(request().delete()) response.status shouldBe Status.FORBIDDEN } } @@ -108,7 +108,7 @@ class AuthISpec extends IntegrationBaseSpec { AuthStub.unauthorisedOther() } - val response: WSResponse = await(request().delete) + val response: WSResponse = await(request().delete()) response.status shouldBe Status.FORBIDDEN } } diff --git a/it/v1/endpoints/CreateMarriageAllowanceControllerISpec.scala b/it/v1/endpoints/CreateMarriageAllowanceControllerISpec.scala index d0a4e31..512c14e 100644 --- a/it/v1/endpoints/CreateMarriageAllowanceControllerISpec.scala +++ b/it/v1/endpoints/CreateMarriageAllowanceControllerISpec.scala @@ -19,12 +19,13 @@ package v1.endpoints import com.github.tomakehurst.wiremock.stubbing.StubMapping import play.api.http.HeaderNames.ACCEPT import play.api.http.Status._ -import play.api.libs.json.{JsValue, Json} +import play.api.libs.json.{JsResult, JsSuccess, JsValue, Json} import play.api.libs.ws.{WSRequest, WSResponse} import play.api.test.Helpers.AUTHORIZATION import support.IntegrationBaseSpec import v1.models.errors._ -import v1.stubs.{AuditStub, AuthStub, DesStub, MtdIdLookupStub} +import v1.stubs.{AuditStub, AuthStub, DownstreamStub, MtdIdLookupStub} + class CreateMarriageAllowanceControllerISpec extends IntegrationBaseSpec { @@ -48,7 +49,7 @@ class CreateMarriageAllowanceControllerISpec extends IntegrationBaseSpec { def uri: String = s"/marriage-allowance/$nino1" - def Ifs2Uri: String = s"/income-tax/marriage-allowance/claim/nino/$nino1" + def ifs2Uri: String = s"/income-tax/marriage-allowance/claim/nino/$nino1" def setupStubs(): StubMapping @@ -70,7 +71,7 @@ class CreateMarriageAllowanceControllerISpec extends IntegrationBaseSpec { AuditStub.audit() AuthStub.authorised() MtdIdLookupStub.ninoFound(nino1) - DesStub.onSuccess(DesStub.POST, Ifs2Uri, NO_CONTENT) + DownstreamStub.onSuccess(DownstreamStub.POST, ifs2Uri, NO_CONTENT) } val response: WSResponse = await(request().post(requestBodyJson)) @@ -134,14 +135,6 @@ class CreateMarriageAllowanceControllerISpec extends IntegrationBaseSpec { """.stripMargin ) - val nonsenseRequestBodyJson: JsValue = Json.parse( - """ - |{ - | "field": "value" - |} - """.stripMargin - ) - val emptyBodyJson: JsValue = Json.parse( """ |{ @@ -193,9 +186,6 @@ class CreateMarriageAllowanceControllerISpec extends IntegrationBaseSpec { """.stripMargin ) - val nonsenseBodyPaths: MtdError = - RuleIncorrectOrEmptyBodyError.copy(paths = Some(Seq("/spouseOrCivilPartnerNino", "/spouseOrCivilPartnerSurname"))) - "validation error" when { def validationErrorTest(requestNino: String, requestBody: JsValue, expectedStatus: Int, expectedBody: MtdError): Unit = { s"validation $requestNino fails with ${expectedBody.code} error" in new Test { @@ -216,7 +206,6 @@ class CreateMarriageAllowanceControllerISpec extends IntegrationBaseSpec { val input = Seq( ("AA1123A", validRequestBodyJson, BAD_REQUEST, NinoFormatError), - ("AA123456A", nonsenseRequestBodyJson, BAD_REQUEST, nonsenseBodyPaths), ("AA123458A", emptyBodyJson, BAD_REQUEST, RuleIncorrectOrEmptyBodyError), ("AA123457A", invalidNinoBodyJson, BAD_REQUEST, PartnerNinoFormatError), ("AA123457A", invalidFirstNameBodyJson, BAD_REQUEST, PartnerFirstNameFormatError), @@ -224,6 +213,38 @@ class CreateMarriageAllowanceControllerISpec extends IntegrationBaseSpec { ("AA123459A", invalidDobBodyJson, BAD_REQUEST, PartnerDoBFormatError), ) input.foreach(args => (validationErrorTest _).tupled(args)) + + "with complex body format errors" in new Test { + val nonsenseBodyPaths: List[String] = List("/spouseOrCivilPartnerNino", "/spouseOrCivilPartnerSurname") + + val nonsenseRequestBodyJson: JsValue = Json.parse( + """ + |{ + | "field": "value" + |} + """.stripMargin + ) + + override def setupStubs(): StubMapping = { + AuthStub.authorised() + MtdIdLookupStub.ninoFound(nino1) + } + + val response: WSResponse = await(request().post(nonsenseRequestBodyJson)) + response.status shouldBe BAD_REQUEST + + val responseErrorCode: JsResult[String] = (response.json \ "code").validate[String] + val responseErrorMessage: JsResult[String] = (response.json \ "message").validate[String] + val responseErrorPaths: JsResult[Seq[String]] = (response.json \ "paths").validate[Seq[String]] + + responseErrorCode shouldBe a[JsSuccess[_]] + responseErrorMessage shouldBe a[JsSuccess[_]] + responseErrorPaths shouldBe a[JsSuccess[_]] + + responseErrorCode.get shouldBe RuleIncorrectOrEmptyBodyError.code + responseErrorMessage.get shouldBe RuleIncorrectOrEmptyBodyError.message + responseErrorPaths.get should contain.allElementsOf(nonsenseBodyPaths) + } } "ifs service error" when { @@ -233,7 +254,7 @@ class CreateMarriageAllowanceControllerISpec extends IntegrationBaseSpec { override def setupStubs(): StubMapping = { AuthStub.authorised() MtdIdLookupStub.ninoFound(nino1) - DesStub.onError(DesStub.POST, Ifs2Uri, ifsStatus, errorBody(ifsCode)) + DownstreamStub.onError(DownstreamStub.POST, ifs2Uri, ifsStatus, errorBody(ifsCode)) } val response: WSResponse = await(request().post(requestBodyJson)) @@ -251,25 +272,25 @@ class CreateMarriageAllowanceControllerISpec extends IntegrationBaseSpec { """.stripMargin val input = Seq( - (BAD_REQUEST, "INVALID_IDTYPE", INTERNAL_SERVER_ERROR, DownstreamError), + (BAD_REQUEST, "INVALID_IDTYPE", INTERNAL_SERVER_ERROR, InternalError), (BAD_REQUEST, "INVALID_IDVALUE", BAD_REQUEST, NinoFormatError), - (BAD_REQUEST, "INVALID_PAYLOAD", INTERNAL_SERVER_ERROR, DownstreamError), - (BAD_REQUEST, "INVALID_CORRELATIONID", INTERNAL_SERVER_ERROR, DownstreamError), - (NOT_FOUND, "END_DATE_CODE_NOT_FOUND", INTERNAL_SERVER_ERROR, DownstreamError), + (BAD_REQUEST, "INVALID_PAYLOAD", INTERNAL_SERVER_ERROR, InternalError), + (BAD_REQUEST, "INVALID_CORRELATIONID", INTERNAL_SERVER_ERROR, InternalError), + (NOT_FOUND, "END_DATE_CODE_NOT_FOUND", INTERNAL_SERVER_ERROR, InternalError), (NOT_FOUND, "NINO_OR_TRN_NOT_FOUND", FORBIDDEN, RuleInvalidRequestError), - (UNPROCESSABLE_ENTITY, "INVALID_ACTUAL_END_DATE", INTERNAL_SERVER_ERROR, DownstreamError), - (UNPROCESSABLE_ENTITY, "INVALID_PARTICIPANT_END_DATE", INTERNAL_SERVER_ERROR, DownstreamError), - (UNPROCESSABLE_ENTITY, "INVALID_PARTICIPANT_START_DATE", INTERNAL_SERVER_ERROR, DownstreamError), + (UNPROCESSABLE_ENTITY, "INVALID_ACTUAL_END_DATE", INTERNAL_SERVER_ERROR, InternalError), + (UNPROCESSABLE_ENTITY, "INVALID_PARTICIPANT_END_DATE", INTERNAL_SERVER_ERROR, InternalError), + (UNPROCESSABLE_ENTITY, "INVALID_PARTICIPANT_START_DATE", INTERNAL_SERVER_ERROR, InternalError), (UNPROCESSABLE_ENTITY, "DECEASED_PARTICIPANT", FORBIDDEN, RuleDeceasedRecipientError), - (UNPROCESSABLE_ENTITY, "INVALID_RELATIONSHIP_CODE", INTERNAL_SERVER_ERROR, DownstreamError), - (UNPROCESSABLE_ENTITY, "PARTICIPANT1_CANNOT_BE_UPDATED", INTERNAL_SERVER_ERROR, DownstreamError), - (UNPROCESSABLE_ENTITY, "PARTICIPANT2_CANNOT_BE_UPDATED", INTERNAL_SERVER_ERROR, DownstreamError), + (UNPROCESSABLE_ENTITY, "INVALID_RELATIONSHIP_CODE", INTERNAL_SERVER_ERROR, InternalError), + (UNPROCESSABLE_ENTITY, "PARTICIPANT1_CANNOT_BE_UPDATED", INTERNAL_SERVER_ERROR, InternalError), + (UNPROCESSABLE_ENTITY, "PARTICIPANT2_CANNOT_BE_UPDATED", INTERNAL_SERVER_ERROR, InternalError), (UNPROCESSABLE_ENTITY, "RELATIONSHIP_ALREADY_EXISTS", FORBIDDEN, RuleActiveMarriageAllowanceClaimError), - (UNPROCESSABLE_ENTITY, "CONFIDENCE_CHECK_FAILED", INTERNAL_SERVER_ERROR, DownstreamError), - (UNPROCESSABLE_ENTITY, "CONFIDENCE_CHECK_SURNAME_MISSED", INTERNAL_SERVER_ERROR, DownstreamError), - (INTERNAL_SERVER_ERROR, "SERVER_ERROR", INTERNAL_SERVER_ERROR, DownstreamError), - (BAD_GATEWAY, "BAD_GATEWAY", INTERNAL_SERVER_ERROR, DownstreamError), - (SERVICE_UNAVAILABLE, "SERVICE_UNAVAILABLE", INTERNAL_SERVER_ERROR, DownstreamError) + (UNPROCESSABLE_ENTITY, "CONFIDENCE_CHECK_FAILED", INTERNAL_SERVER_ERROR, InternalError), + (UNPROCESSABLE_ENTITY, "CONFIDENCE_CHECK_SURNAME_MISSED", INTERNAL_SERVER_ERROR, InternalError), + (INTERNAL_SERVER_ERROR, "SERVER_ERROR", INTERNAL_SERVER_ERROR, InternalError), + (BAD_GATEWAY, "BAD_GATEWAY", INTERNAL_SERVER_ERROR, InternalError), + (SERVICE_UNAVAILABLE, "SERVICE_UNAVAILABLE", INTERNAL_SERVER_ERROR, InternalError) ) input.foreach(args => (serviceErrorTest _).tupled(args)) } diff --git a/it/v1/endpoints/DeleteDisclosuresControllerISpec.scala b/it/v1/endpoints/DeleteDisclosuresControllerISpec.scala index 03e0a5d..3ed6141 100644 --- a/it/v1/endpoints/DeleteDisclosuresControllerISpec.scala +++ b/it/v1/endpoints/DeleteDisclosuresControllerISpec.scala @@ -24,7 +24,7 @@ import play.api.libs.ws.{WSRequest, WSResponse} import play.api.test.Helpers.AUTHORIZATION import support.IntegrationBaseSpec import v1.models.errors._ -import v1.stubs.{AuditStub, AuthStub, DesStub, MtdIdLookupStub} +import v1.stubs.{AuditStub, AuthStub, DownstreamStub, MtdIdLookupStub} class DeleteDisclosuresControllerISpec extends IntegrationBaseSpec { @@ -57,10 +57,10 @@ class DeleteDisclosuresControllerISpec extends IntegrationBaseSpec { AuditStub.audit() AuthStub.authorised() MtdIdLookupStub.ninoFound(nino) - DesStub.onSuccess(DesStub.DELETE, ifs1Uri, NO_CONTENT) + DownstreamStub.onSuccess(DownstreamStub.DELETE, ifs1Uri, NO_CONTENT) } - val response: WSResponse = await(request().delete) + val response: WSResponse = await(request().delete()) response.status shouldBe NO_CONTENT response.body shouldBe "" response.header("Content-Type") shouldBe Some("application/json") @@ -82,7 +82,7 @@ class DeleteDisclosuresControllerISpec extends IntegrationBaseSpec { MtdIdLookupStub.ninoFound(nino) } - val response: WSResponse = await(request().delete) + val response: WSResponse = await(request().delete()) response.status shouldBe expectedStatus response.json shouldBe Json.toJson(expectedBody) response.header("Content-Type") shouldBe Some("application/json") @@ -106,10 +106,10 @@ class DeleteDisclosuresControllerISpec extends IntegrationBaseSpec { AuditStub.audit() AuthStub.authorised() MtdIdLookupStub.ninoFound(nino) - DesStub.onError(DesStub.DELETE, ifs1Uri, ifsStatus, errorBody(ifsCode)) + DownstreamStub.onError(DownstreamStub.DELETE, ifs1Uri, ifsStatus, errorBody(ifsCode)) } - val response: WSResponse = await(request().delete) + val response: WSResponse = await(request().delete()) response.status shouldBe expectedStatus response.json shouldBe Json.toJson(expectedBody) response.header("Content-Type") shouldBe Some("application/json") @@ -127,11 +127,11 @@ class DeleteDisclosuresControllerISpec extends IntegrationBaseSpec { val input = Seq( (BAD_REQUEST, "INVALID_TAXABLE_ENTITY_ID", BAD_REQUEST, NinoFormatError), (BAD_REQUEST, "INVALID_TAX_YEAR", BAD_REQUEST, TaxYearFormatError), - (BAD_REQUEST, "INVALID_CORRELATIONID", INTERNAL_SERVER_ERROR, DownstreamError), + (BAD_REQUEST, "INVALID_CORRELATIONID", INTERNAL_SERVER_ERROR, InternalError), (NOT_FOUND, "NO_DATA_FOUND", NOT_FOUND, NotFoundError), (UNPROCESSABLE_ENTITY, "VOLUNTARY_CLASS2_CANNOT_BE_CHANGED", FORBIDDEN, RuleVoluntaryClass2CannotBeChangedError), - (INTERNAL_SERVER_ERROR, "SERVER_ERROR", INTERNAL_SERVER_ERROR, DownstreamError), - (SERVICE_UNAVAILABLE, "SERVICE_UNAVAILABLE", INTERNAL_SERVER_ERROR, DownstreamError) + (INTERNAL_SERVER_ERROR, "SERVER_ERROR", INTERNAL_SERVER_ERROR, InternalError), + (SERVICE_UNAVAILABLE, "SERVICE_UNAVAILABLE", INTERNAL_SERVER_ERROR, InternalError) ) input.foreach(args => (serviceErrorTest _).tupled(args)) } diff --git a/it/v1/endpoints/RetrieveDisclosuresControllerISpec.scala b/it/v1/endpoints/RetrieveDisclosuresControllerISpec.scala index d7d0a9a..67100b6 100644 --- a/it/v1/endpoints/RetrieveDisclosuresControllerISpec.scala +++ b/it/v1/endpoints/RetrieveDisclosuresControllerISpec.scala @@ -25,7 +25,7 @@ import play.api.test.Helpers.AUTHORIZATION import support.IntegrationBaseSpec import v1.fixtures.RetrieveDisclosuresControllerFixture import v1.models.errors._ -import v1.stubs.{AuditStub, AuthStub, DesStub, MtdIdLookupStub} +import v1.stubs.{AuditStub, AuthStub, DownstreamStub, MtdIdLookupStub} class RetrieveDisclosuresControllerISpec extends IntegrationBaseSpec { @@ -62,10 +62,10 @@ class RetrieveDisclosuresControllerISpec extends IntegrationBaseSpec { AuditStub.audit() AuthStub.authorised() MtdIdLookupStub.ninoFound(nino) - DesStub.onSuccess(DesStub.GET, ifs1Uri, OK, ifsResponse) + DownstreamStub.onSuccess(DownstreamStub.GET, ifs1Uri, OK, ifsResponse) } - val response: WSResponse = await(request.get) + val response: WSResponse = await(request.get()) response.status shouldBe OK response.json shouldBe mtdResponse response.header("Content-Type") shouldBe Some("application/json") @@ -87,7 +87,7 @@ class RetrieveDisclosuresControllerISpec extends IntegrationBaseSpec { MtdIdLookupStub.ninoFound(nino) } - val response: WSResponse = await(request.get) + val response: WSResponse = await(request.get()) response.status shouldBe expectedStatus response.json shouldBe Json.toJson(expectedBody) response.header("Content-Type") shouldBe Some("application/json") @@ -111,10 +111,10 @@ class RetrieveDisclosuresControllerISpec extends IntegrationBaseSpec { AuditStub.audit() AuthStub.authorised() MtdIdLookupStub.ninoFound(nino) - DesStub.onError(DesStub.GET, ifs1Uri, ifsStatus, errorBody(ifsCode)) + DownstreamStub.onError(DownstreamStub.GET, ifs1Uri, ifsStatus, errorBody(ifsCode)) } - val response: WSResponse = await(request.get) + val response: WSResponse = await(request.get()) response.status shouldBe expectedStatus response.json shouldBe Json.toJson(expectedBody) response.header("Content-Type") shouldBe Some("application/json") @@ -132,10 +132,10 @@ class RetrieveDisclosuresControllerISpec extends IntegrationBaseSpec { val input = Seq( (BAD_REQUEST, "INVALID_TAXABLE_ENTITY_ID", BAD_REQUEST, NinoFormatError), (BAD_REQUEST, "INVALID_TAX_YEAR", BAD_REQUEST, TaxYearFormatError), - (BAD_REQUEST, "INVALID_CORRELATIONID", INTERNAL_SERVER_ERROR, DownstreamError), + (BAD_REQUEST, "INVALID_CORRELATIONID", INTERNAL_SERVER_ERROR, InternalError), (NOT_FOUND, "NO_DATA_FOUND", NOT_FOUND, NotFoundError), - (INTERNAL_SERVER_ERROR, "SERVER_ERROR", INTERNAL_SERVER_ERROR, DownstreamError), - (SERVICE_UNAVAILABLE, "SERVICE_UNAVAILABLE", INTERNAL_SERVER_ERROR, DownstreamError) + (INTERNAL_SERVER_ERROR, "SERVER_ERROR", INTERNAL_SERVER_ERROR, InternalError), + (SERVICE_UNAVAILABLE, "SERVICE_UNAVAILABLE", INTERNAL_SERVER_ERROR, InternalError) ) input.foreach(args => (serviceErrorTest _).tupled(args)) } diff --git a/it/v1/stubs/DesStub.scala b/it/v1/stubs/DownstreamStub.scala similarity index 97% rename from it/v1/stubs/DesStub.scala rename to it/v1/stubs/DownstreamStub.scala index baa94e9..ab43b0e 100644 --- a/it/v1/stubs/DesStub.scala +++ b/it/v1/stubs/DownstreamStub.scala @@ -20,7 +20,7 @@ import com.github.tomakehurst.wiremock.stubbing.StubMapping import play.api.libs.json.JsValue import support.WireMockMethods -object DesStub extends WireMockMethods { +object DownstreamStub extends WireMockMethods { def onSuccess(method: HTTPMethod, uri: String, status: Int, body: JsValue): StubMapping = { when(method = method, uri = uri) diff --git a/test/definition/ApiDefinitionFactorySpec.scala b/test/definition/ApiDefinitionFactorySpec.scala index d4588c7..19142bf 100644 --- a/test/definition/ApiDefinitionFactorySpec.scala +++ b/test/definition/ApiDefinitionFactorySpec.scala @@ -44,7 +44,7 @@ class ApiDefinitionFactorySpec extends UnitSpec { def testDefinitionWithConfidence(confidenceLevelConfig: ConfidenceLevelConfig): Unit = new Test { MockAppConfig.apiStatus returns "1.0" MockAppConfig.endpointsEnabled returns true - MockAppConfig.confidenceLevelCheckEnabled returns confidenceLevelConfig anyNumberOfTimes() + MockAppConfig.confidenceLevelCheckEnabled.returns(confidenceLevelConfig).anyNumberOfTimes() val readScope: String = "read:self-assessment" val writeScope: String = "write:self-assessment" diff --git a/test/mocks/MockAppConfig.scala b/test/mocks/MockAppConfig.scala index 361f842..7fb819c 100644 --- a/test/mocks/MockAppConfig.scala +++ b/test/mocks/MockAppConfig.scala @@ -27,28 +27,28 @@ trait MockAppConfig extends MockFactory { object MockAppConfig { // MTD ID Lookup Config - def mtdIdBaseUrl: CallHandler[String] = (mockAppConfig.mtdIdBaseUrl _: () => String).expects() + def mtdIdBaseUrl: CallHandler[String] = (() => mockAppConfig.mtdIdBaseUrl: String).expects() // IFS-1 Config - def ifs1BaseUrl: CallHandler[String] = (mockAppConfig.ifs1BaseUrl _: () => String).expects() - def ifs1Token: CallHandler[String] = (mockAppConfig.ifs1Token _).expects() - def ifs1Environment: CallHandler[String] = (mockAppConfig.ifs1Env _).expects() - def ifs1EnvironmentHeaders: CallHandler[Option[Seq[String]]] = (mockAppConfig.ifs1EnvironmentHeaders _).expects() + def ifs1BaseUrl: CallHandler[String] = (() => mockAppConfig.ifs1BaseUrl: String).expects() + def ifs1Token: CallHandler[String] = (() => mockAppConfig.ifs1Token: String).expects() + def ifs1Environment: CallHandler[String] = (() => mockAppConfig.ifs1Env: String).expects() + def ifs1EnvironmentHeaders: CallHandler[Option[Seq[String]]] = (() => mockAppConfig.ifs1EnvironmentHeaders: Option[Seq[String]]).expects() // IFS-2 Config - def ifs2BaseUrl: CallHandler[String] = (mockAppConfig.ifs2BaseUrl _: () => String).expects() - def ifs2Token: CallHandler[String] = (mockAppConfig.ifs2Token _).expects() - def ifs2Environment: CallHandler[String] = (mockAppConfig.ifs2Env _).expects() - def ifs2EnvironmentHeaders: CallHandler[Option[Seq[String]]] = (mockAppConfig.ifs2EnvironmentHeaders _).expects() + def ifs2BaseUrl: CallHandler[String] = (() => mockAppConfig.ifs2BaseUrl: String).expects() + def ifs2Token: CallHandler[String] = (() => mockAppConfig.ifs2Token: String).expects() + def ifs2Environment: CallHandler[String] = (() => mockAppConfig.ifs2Env: String).expects() + def ifs2EnvironmentHeaders: CallHandler[Option[Seq[String]]] = (() => mockAppConfig.ifs2EnvironmentHeaders: Option[Seq[String]]).expects() // Business Rule Config - def minimumPermittedTaxYear: CallHandler[Int] = (mockAppConfig.minimumPermittedTaxYear _).expects() + def minimumPermittedTaxYear: CallHandler[Int] = (() => mockAppConfig.minimumPermittedTaxYear: Int).expects() // API Config - def featureSwitch: CallHandler[Option[Configuration]] = (mockAppConfig.featureSwitch _: () => Option[Configuration]).expects() - def apiGatewayContext: CallHandler[String] = (mockAppConfig.apiGatewayContext _: () => String).expects() + def featureSwitch: CallHandler[Option[Configuration]] = (() => mockAppConfig.featureSwitch: Option[Configuration]).expects() + def apiGatewayContext: CallHandler[String] = (() => mockAppConfig.apiGatewayContext: String).expects() def apiStatus: CallHandler[String] = (mockAppConfig.apiStatus: String => String).expects("1.0") def endpointsEnabled: CallHandler[Boolean] = (mockAppConfig.endpointsEnabled: String => Boolean).expects("1.0") - def confidenceLevelCheckEnabled: CallHandler[ConfidenceLevelConfig] = (mockAppConfig.confidenceLevelConfig _: () => ConfidenceLevelConfig).expects() + def confidenceLevelCheckEnabled: CallHandler[ConfidenceLevelConfig] = (() => mockAppConfig.confidenceLevelConfig: ConfidenceLevelConfig).expects() } } diff --git a/test/utils/ErrorHandlerSpec.scala b/test/utils/ErrorHandlerSpec.scala index 99c7c75..9f967cf 100644 --- a/test/utils/ErrorHandlerSpec.scala +++ b/test/utils/ErrorHandlerSpec.scala @@ -160,7 +160,7 @@ class ErrorHandlerSpec extends UnitSpec with GuiceOneAppPerSuite { private val result = handler.onServerError(requestHeader, new Exception with NoStackTrace) status(result) shouldBe INTERNAL_SERVER_ERROR - contentAsJson(result) shouldBe Json.toJson(DownstreamError) + contentAsJson(result) shouldBe Json.toJson(InternalError) } } } diff --git a/test/v1/connectors/BaseDownstreamConnectorSpec.scala b/test/v1/connectors/BaseDownstreamConnectorSpec.scala index 5fa3902..317d5a7 100644 --- a/test/v1/connectors/BaseDownstreamConnectorSpec.scala +++ b/test/v1/connectors/BaseDownstreamConnectorSpec.scala @@ -138,7 +138,7 @@ class BaseDownstreamConnectorSpec extends ConnectorSpec { "AnotherHeader" -> "HeaderValue" ) - "making a HTTP request to a downstream service (i.e DES)" must { + "making a HTTP request to a downstream service (i.e DOWNSTREAM)" must { ifs2TestHttpMethods(dummyIfs1HeaderCarrierConfig, requiredHeaders, excludedHeaders, Some(allowedIfs1Headers)) "exclude all `otherHeaders` when no external service header allow-list is found" should { diff --git a/test/v1/connectors/MtdIdLookupConnectorSpec.scala b/test/v1/connectors/MtdIdLookupConnectorSpec.scala index 8d2f15a..a04e939 100644 --- a/test/v1/connectors/MtdIdLookupConnectorSpec.scala +++ b/test/v1/connectors/MtdIdLookupConnectorSpec.scala @@ -18,7 +18,7 @@ package v1.connectors import mocks.MockAppConfig import v1.mocks.MockHttpClient -import v1.models.errors.DownstreamError +import v1.models.errors.InternalError import scala.concurrent.Future @@ -53,10 +53,10 @@ class MtdIdLookupConnectorSpec extends ConnectorSpec { MockedHttpClient.get[MtdIdLookupOutcome]( url = s"$baseUrl/mtd-identifier-lookup/nino/$nino", config = dummyIfs1HeaderCarrierConfig - ).returns(Future.successful(Left(DownstreamError))) + ).returns(Future.successful(Left(InternalError))) val result: MtdIdLookupOutcome = await(connector.getMtdId(nino)) - result shouldBe Left(DownstreamError) + result shouldBe Left(InternalError) } } } diff --git a/test/v1/connectors/httpparsers/MtdIdLookupHttpParserSpec.scala b/test/v1/connectors/httpparsers/MtdIdLookupHttpParserSpec.scala index 7a10aab..0a7f5e2 100644 --- a/test/v1/connectors/httpparsers/MtdIdLookupHttpParserSpec.scala +++ b/test/v1/connectors/httpparsers/MtdIdLookupHttpParserSpec.scala @@ -23,7 +23,7 @@ import support.UnitSpec import uk.gov.hmrc.http.HttpResponse import v1.connectors.MtdIdLookupOutcome import v1.connectors.httpparsers.MtdIdLookupHttpParser.mtdIdLookupHttpReads -import v1.models.errors.{DownstreamError, InvalidBearerTokenError, NinoFormatError} +import v1.models.errors.{InternalError, InvalidBearerTokenError, NinoFormatError} class MtdIdLookupHttpParserSpec extends UnitSpec { @@ -49,21 +49,21 @@ class MtdIdLookupHttpParserSpec extends UnitSpec { val response = HttpResponse(OK, invalidJson.toString()) val result: MtdIdLookupOutcome = mtdIdLookupHttpReads.read(method, url, response) - result shouldBe Left(DownstreamError) + result shouldBe Left(InternalError) } "backend doesn't return any data" in { val response = HttpResponse(OK, None.orNull) val result: MtdIdLookupOutcome = mtdIdLookupHttpReads.read(method, url, response) - result shouldBe Left(DownstreamError) + result shouldBe Left(InternalError) } "the json cannot be read" in { val response = HttpResponse(OK, None.orNull) val result: MtdIdLookupOutcome = mtdIdLookupHttpReads.read(method, url, response) - result shouldBe Left(DownstreamError) + result shouldBe Left(InternalError) } } @@ -90,7 +90,7 @@ class MtdIdLookupHttpParserSpec extends UnitSpec { val response = HttpResponse(INTERNAL_SERVER_ERROR, None.orNull) val result: MtdIdLookupOutcome = mtdIdLookupHttpReads.read(method, url, response) - result shouldBe Left(DownstreamError) + result shouldBe Left(InternalError) } } } diff --git a/test/v1/connectors/httpparsers/StandardDesHttpParserSpec.scala b/test/v1/connectors/httpparsers/StandardDownstreamHttpParserSpec.scala similarity index 83% rename from test/v1/connectors/httpparsers/StandardDesHttpParserSpec.scala rename to test/v1/connectors/httpparsers/StandardDownstreamHttpParserSpec.scala index efce0f8..5eb6812 100644 --- a/test/v1/connectors/httpparsers/StandardDesHttpParserSpec.scala +++ b/test/v1/connectors/httpparsers/StandardDownstreamHttpParserSpec.scala @@ -24,6 +24,7 @@ import v1.connectors.DownstreamOutcome import v1.models.errors._ import v1.models.outcomes.ResponseWrapper + // WLOG if Reads tested elsewhere case class SomeModel(data: String) @@ -31,37 +32,37 @@ object SomeModel { implicit val reads: Reads[SomeModel] = Json.reads } -class StandardDesHttpParserSpec extends UnitSpec { +class StandardDownstreamHttpParserSpec extends UnitSpec { val method = "POST" val url = "test-url" val correlationId = "a1e8057e-fbbc-47a8-a8b4-78d9f015c253" - import v1.connectors.httpparsers.StandardDesHttpParser._ + import v1.connectors.httpparsers.StandardDownstreamHttpParser._ val httpReads: HttpReads[DownstreamOutcome[Unit]] = implicitly val data = "someData" - val desExpectedJson: JsValue = Json.obj("data" -> data) + val downstreamExpectedJson: JsValue = Json.obj("data" -> data) - private val desModel = SomeModel(data) - private val desResponse = ResponseWrapper(correlationId, desModel) + private val downstreamModel = SomeModel(data) + private val downstreamResponse = ResponseWrapper(correlationId, downstreamModel) "The generic HTTP parser" when { "no status code is specified" must { val httpReads: HttpReads[DownstreamOutcome[SomeModel]] = implicitly - "return a Right DES response containing the model object if the response json corresponds to a model object" in { - val httpResponse = HttpResponse(OK, desExpectedJson.toString(), Map("CorrelationId" -> Seq(correlationId))) + "return a Right DOWNSTREAM response containing the model object if the response json corresponds to a model object" in { + val httpResponse = HttpResponse(OK, downstreamExpectedJson.toString(), Map("CorrelationId" -> Seq(correlationId))) - httpReads.read(method, url, httpResponse) shouldBe Right(desResponse) + httpReads.read(method, url, httpResponse) shouldBe Right(downstreamResponse) } "return an outbound error if a model object cannot be read from the response json" in { val badFieldTypeJson: JsValue = Json.obj("incomeSourceId" -> 1234, "incomeSourceName" -> 1234) val httpResponse = HttpResponse(OK, badFieldTypeJson.toString(), Map("CorrelationId" -> Seq(correlationId))) - val expected = ResponseWrapper(correlationId, OutboundError(DownstreamError)) + val expected = ResponseWrapper(correlationId, OutboundError(InternalError)) httpReads.read(method, url, httpResponse) shouldBe Left(expected) } @@ -77,9 +78,9 @@ class StandardDesHttpParserSpec extends UnitSpec { implicit val successCode: SuccessCode = SuccessCode(PARTIAL_CONTENT) val httpReads: HttpReads[DownstreamOutcome[SomeModel]] = implicitly - val httpResponse = HttpResponse(PARTIAL_CONTENT, desExpectedJson.toString(), Map("CorrelationId" -> Seq(correlationId))) + val httpResponse = HttpResponse(PARTIAL_CONTENT, downstreamExpectedJson.toString(), Map("CorrelationId" -> Seq(correlationId))) - httpReads.read(method, url, httpResponse) shouldBe Right(desResponse) + httpReads.read(method, url, httpResponse) shouldBe Right(downstreamResponse) } } } @@ -89,7 +90,7 @@ class StandardDesHttpParserSpec extends UnitSpec { val httpReads: HttpReads[DownstreamOutcome[Unit]] = implicitly "receiving a 204 response" should { - "return a Right DesResponse with the correct correlationId and no responseData" in { + "return a Right DownstreamResponse with the correct correlationId and no responseData" in { val httpResponse = HttpResponse(NO_CONTENT, body = "", headers = Map("CorrelationId" -> Seq(correlationId))) httpReads.read(method, url, httpResponse) shouldBe Right(ResponseWrapper(correlationId, ())) @@ -156,21 +157,21 @@ class StandardDesHttpParserSpec extends UnitSpec { "be able to parse a single error" in { val httpResponse = HttpResponse(responseCode, singleErrorJson.toString(), Map("CorrelationId" -> Seq(correlationId))) - httpReads.read(method, url, httpResponse) shouldBe Left(ResponseWrapper(correlationId, DesErrors.single(DesErrorCode("CODE")))) + httpReads.read(method, url, httpResponse) shouldBe Left(ResponseWrapper(correlationId, DownstreamErrors.single(DownstreamErrorCode("CODE")))) } "be able to parse multiple errors" in { val httpResponse = HttpResponse(responseCode, multipleErrorsJson.toString(), Map("CorrelationId" -> Seq(correlationId))) httpReads.read(method, url, httpResponse) shouldBe { - Left(ResponseWrapper(correlationId, DesErrors(List(DesErrorCode("CODE 1"), DesErrorCode("CODE 2"))))) + Left(ResponseWrapper(correlationId, DownstreamErrors(List(DownstreamErrorCode("CODE 1"), DownstreamErrorCode("CODE 2"))))) } } "return an outbound error when the error returned doesn't match the Error model" in { val httpResponse = HttpResponse(responseCode, malformedErrorJson.toString(), Map("CorrelationId" -> Seq(correlationId))) - httpReads.read(method, url, httpResponse) shouldBe Left(ResponseWrapper(correlationId, OutboundError(DownstreamError))) + httpReads.read(method, url, httpResponse) shouldBe Left(ResponseWrapper(correlationId, OutboundError(InternalError))) } } ) @@ -181,13 +182,13 @@ class StandardDesHttpParserSpec extends UnitSpec { "return an outbound error when the error returned matches the Error model" in { val httpResponse = HttpResponse(responseCode, singleErrorJson.toString(), Map("CorrelationId" -> Seq(correlationId))) - httpReads.read(method, url, httpResponse) shouldBe Left(ResponseWrapper(correlationId, OutboundError(DownstreamError))) + httpReads.read(method, url, httpResponse) shouldBe Left(ResponseWrapper(correlationId, OutboundError(InternalError))) } "return an outbound error when the error returned doesn't match the Error model" in { val httpResponse = HttpResponse(responseCode, malformedErrorJson.toString(), Map("CorrelationId" -> Seq(correlationId))) - httpReads.read(method, url, httpResponse) shouldBe Left(ResponseWrapper(correlationId, OutboundError(DownstreamError))) + httpReads.read(method, url, httpResponse) shouldBe Left(ResponseWrapper(correlationId, OutboundError(InternalError))) } }) @@ -197,20 +198,18 @@ class StandardDesHttpParserSpec extends UnitSpec { "return an outbound error when the error returned matches the Error model" in { val httpResponse = HttpResponse(responseCode, singleErrorJson.toString(), Map("CorrelationId" -> Seq(correlationId))) - httpReads.read(method, url, httpResponse) shouldBe Left(ResponseWrapper(correlationId, OutboundError(DownstreamError))) + httpReads.read(method, url, httpResponse) shouldBe Left(ResponseWrapper(correlationId, OutboundError(InternalError))) } "return an outbound error when the error returned doesn't match the Error model" in { val httpResponse = HttpResponse(responseCode, malformedErrorJson.toString(), Map("CorrelationId" -> Seq(correlationId))) - httpReads.read(method, url, httpResponse) shouldBe Left(ResponseWrapper(correlationId, OutboundError(DownstreamError))) + httpReads.read(method, url, httpResponse) shouldBe Left(ResponseWrapper(correlationId, OutboundError(InternalError))) } } private def handleBvrsCorrectly[A](httpReads: HttpReads[DownstreamOutcome[A]]): Unit = { - - val singleBvrJson = Json.parse( """ |{ @@ -233,7 +232,7 @@ class StandardDesHttpParserSpec extends UnitSpec { httpReads.read(method, url, httpResponse) shouldBe Left(ResponseWrapper(correlationId, - OutboundError(BVRError, Some(Seq(MtdError("BVR1", ""), MtdError("BVR2", "")))))) + OutboundError(BVRError, Some(List(MtdError("BVR1", ""), MtdError("BVR2", "")))))) } } } diff --git a/test/v1/controllers/AmendDisclosuresControllerSpec.scala b/test/v1/controllers/AmendDisclosuresControllerSpec.scala index ac49bd9..ad34824 100644 --- a/test/v1/controllers/AmendDisclosuresControllerSpec.scala +++ b/test/v1/controllers/AmendDisclosuresControllerSpec.scala @@ -173,7 +173,7 @@ class AmendDisclosuresControllerSpec header("X-CorrelationId", result) shouldBe Some(correlationId) val auditResponse: AuditResponse = AuditResponse(OK, None, Some(hateoasResponse)) - MockedAuditService.verifyAuditEvent(event(auditResponse)).once + MockedAuditService.verifyAuditEvent(event(auditResponse)).once() } } @@ -192,8 +192,8 @@ class AmendDisclosuresControllerSpec contentAsJson(result) shouldBe Json.toJson(error) header("X-CorrelationId", result) shouldBe Some(correlationId) - val auditResponse: AuditResponse = AuditResponse(expectedStatus, Some(Seq(AuditError(error.code))), None) - MockedAuditService.verifyAuditEvent(event(auditResponse)).once + val auditResponse: AuditResponse = AuditResponse(expectedStatus, Some(List(AuditError(error.code))), None) + MockedAuditService.verifyAuditEvent(event(auditResponse)).once() } } @@ -228,8 +228,8 @@ class AmendDisclosuresControllerSpec contentAsJson(result) shouldBe Json.toJson(mtdError) header("X-CorrelationId", result) shouldBe Some(correlationId) - val auditResponse: AuditResponse = AuditResponse(expectedStatus, Some(Seq(AuditError(mtdError.code))), None) - MockedAuditService.verifyAuditEvent(event(auditResponse)).once + val auditResponse: AuditResponse = AuditResponse(expectedStatus, Some(List(AuditError(mtdError.code))), None) + MockedAuditService.verifyAuditEvent(event(auditResponse)).once() } } @@ -238,7 +238,7 @@ class AmendDisclosuresControllerSpec (TaxYearFormatError, BAD_REQUEST), (NotFoundError, NOT_FOUND), (RuleVoluntaryClass2CannotBeChangedError, FORBIDDEN), - (DownstreamError, INTERNAL_SERVER_ERROR) + (InternalError, INTERNAL_SERVER_ERROR) ) input.foreach(args => (serviceErrors _).tupled(args)) diff --git a/test/v1/controllers/AuthorisedControllerSpec.scala b/test/v1/controllers/AuthorisedControllerSpec.scala index da53d9e..359c86f 100644 --- a/test/v1/controllers/AuthorisedControllerSpec.scala +++ b/test/v1/controllers/AuthorisedControllerSpec.scala @@ -77,7 +77,7 @@ class AuthorisedControllerSpec extends ControllerBaseSpec { MockedEnrolmentsAuthService .authorised(predicate) - .returns(Future.successful(Left(DownstreamError))) + .returns(Future.successful(Left(InternalError))) private val result = target.action(nino)(fakeGetRequest) status(result) shouldBe INTERNAL_SERVER_ERROR @@ -127,7 +127,7 @@ class AuthorisedControllerSpec extends ControllerBaseSpec { MockedMtdIdLookupService .lookup(nino) - .returns(Future.successful(Left(DownstreamError))) + .returns(Future.successful(Left(InternalError))) private val result = target.action(nino)(fakeGetRequest) status(result) shouldBe INTERNAL_SERVER_ERROR diff --git a/test/v1/controllers/CreateMarriageAllowanceControllerSpec.scala b/test/v1/controllers/CreateMarriageAllowanceControllerSpec.scala index f600f7b..0b50fa5 100644 --- a/test/v1/controllers/CreateMarriageAllowanceControllerSpec.scala +++ b/test/v1/controllers/CreateMarriageAllowanceControllerSpec.scala @@ -17,17 +17,17 @@ package v1.controllers import mocks.MockAppConfig -import play.api.libs.json.{ JsValue, Json } -import play.api.mvc.{ AnyContentAsJson, Result } +import play.api.libs.json.{JsValue, Json} +import play.api.mvc.{AnyContentAsJson, Result} import uk.gov.hmrc.http.HeaderCarrier import v1.mocks.MockIdGenerator import v1.mocks.requestParsers.MockCreateMarriageAllowanceRequestParser -import v1.mocks.services.{ MockAuditService, MockCreateMarriageAllowanceService, MockEnrolmentsAuthService, MockMtdIdLookupService } -import v1.models.audit.{ AuditError, AuditEvent, AuditResponse, GenericAuditDetail } +import v1.mocks.services.{MockAuditService, MockCreateMarriageAllowanceService, MockEnrolmentsAuthService, MockMtdIdLookupService} +import v1.models.audit.{AuditError, AuditEvent, AuditResponse, GenericAuditDetail} import v1.models.domain.Nino import v1.models.errors._ import v1.models.outcomes.ResponseWrapper -import v1.models.request.marriageAllowance.{ CreateMarriageAllowanceBody, CreateMarriageAllowanceRawData, CreateMarriageAllowanceRequest } +import v1.models.request.marriageAllowance.{CreateMarriageAllowanceBody, CreateMarriageAllowanceRawData, CreateMarriageAllowanceRequest} import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future @@ -127,7 +127,7 @@ class CreateMarriageAllowanceControllerSpec header("X-CorrelationId", result) shouldBe Some(correlationId) val auditResponse: AuditResponse = AuditResponse(CREATED, None, None) - MockedAuditService.verifyAuditEvent(event(auditResponse)).once + MockedAuditService.verifyAuditEvent(event(auditResponse)).once() } "audit fails" in new Test { @@ -146,7 +146,7 @@ class CreateMarriageAllowanceControllerSpec header("X-CorrelationId", result) shouldBe Some(correlationId) val auditResponse: AuditResponse = AuditResponse(CREATED, None, None) - MockedAuditService.verifyAuditEvent(event(auditResponse), Future.failed(new RuntimeException with NoStackTrace)).once + MockedAuditService.verifyAuditEvent(event(auditResponse), Future.failed(new RuntimeException with NoStackTrace)).once() } } @@ -165,8 +165,8 @@ class CreateMarriageAllowanceControllerSpec contentAsJson(result) shouldBe Json.toJson(error) header("X-CorrelationId", result) shouldBe Some(correlationId) - val auditResponse: AuditResponse = AuditResponse(expectedStatus, Some(Seq(AuditError(error.code))), None) - MockedAuditService.verifyAuditEvent(event(auditResponse)).once + val auditResponse: AuditResponse = AuditResponse(expectedStatus, Some(List(AuditError(error.code))), None) + MockedAuditService.verifyAuditEvent(event(auditResponse)).once() } } @@ -201,8 +201,8 @@ class CreateMarriageAllowanceControllerSpec contentAsJson(result) shouldBe Json.toJson(mtdError) header("X-CorrelationId", result) shouldBe Some(correlationId) - val auditResponse: AuditResponse = AuditResponse(expectedStatus, Some(Seq(AuditError(mtdError.code))), None) - MockedAuditService.verifyAuditEvent(event(auditResponse)).once + val auditResponse: AuditResponse = AuditResponse(expectedStatus, Some(List(AuditError(mtdError.code))), None) + MockedAuditService.verifyAuditEvent(event(auditResponse)).once() } } @@ -211,7 +211,7 @@ class CreateMarriageAllowanceControllerSpec (RuleDeceasedRecipientError, FORBIDDEN), (RuleActiveMarriageAllowanceClaimError, FORBIDDEN), (RuleInvalidRequestError, FORBIDDEN), - (DownstreamError, INTERNAL_SERVER_ERROR) + (InternalError, INTERNAL_SERVER_ERROR) ) input.foreach(args => (serviceErrors _).tupled(args)) diff --git a/test/v1/controllers/DeleteDisclosuresControllerSpec.scala b/test/v1/controllers/DeleteDisclosuresControllerSpec.scala index 5b42482..8b12e5b 100644 --- a/test/v1/controllers/DeleteDisclosuresControllerSpec.scala +++ b/test/v1/controllers/DeleteDisclosuresControllerSpec.scala @@ -105,7 +105,7 @@ class DeleteDisclosuresControllerSpec header("X-CorrelationId", result) shouldBe Some(correlationId) val auditResponse: AuditResponse = AuditResponse(NO_CONTENT, None, None) - MockedAuditService.verifyAuditEvent(event(auditResponse)).once + MockedAuditService.verifyAuditEvent(event(auditResponse)).once() } } @@ -124,8 +124,8 @@ class DeleteDisclosuresControllerSpec contentAsJson(result) shouldBe Json.toJson(error) header("X-CorrelationId", result) shouldBe Some(correlationId) - val auditResponse: AuditResponse = AuditResponse(expectedStatus, Some(Seq(AuditError(error.code))), None) - MockedAuditService.verifyAuditEvent(event(auditResponse)).once + val auditResponse: AuditResponse = AuditResponse(expectedStatus, Some(List(AuditError(error.code))), None) + MockedAuditService.verifyAuditEvent(event(auditResponse)).once() } } @@ -157,8 +157,8 @@ class DeleteDisclosuresControllerSpec contentAsJson(result) shouldBe Json.toJson(mtdError) header("X-CorrelationId", result) shouldBe Some(correlationId) - val auditResponse: AuditResponse = AuditResponse(expectedStatus, Some(Seq(AuditError(mtdError.code))), None) - MockedAuditService.verifyAuditEvent(event(auditResponse)).once + val auditResponse: AuditResponse = AuditResponse(expectedStatus, Some(List(AuditError(mtdError.code))), None) + MockedAuditService.verifyAuditEvent(event(auditResponse)).once() } } @@ -167,7 +167,7 @@ class DeleteDisclosuresControllerSpec (TaxYearFormatError, BAD_REQUEST), (NotFoundError, NOT_FOUND), (RuleVoluntaryClass2CannotBeChangedError, FORBIDDEN), - (DownstreamError, INTERNAL_SERVER_ERROR) + (InternalError, INTERNAL_SERVER_ERROR) ) input.foreach(args => (serviceErrors _).tupled(args)) diff --git a/test/v1/controllers/RetrieveDisclosuresControllerSpec.scala b/test/v1/controllers/RetrieveDisclosuresControllerSpec.scala index 41ff137..c02164f 100644 --- a/test/v1/controllers/RetrieveDisclosuresControllerSpec.scala +++ b/test/v1/controllers/RetrieveDisclosuresControllerSpec.scala @@ -201,7 +201,7 @@ class RetrieveDisclosuresControllerSpec extends ControllerBaseSpec (NinoFormatError, BAD_REQUEST), (TaxYearFormatError, BAD_REQUEST), (NotFoundError, NOT_FOUND), - (DownstreamError, INTERNAL_SERVER_ERROR) + (InternalError, INTERNAL_SERVER_ERROR) ) input.foreach(args => (serviceErrors _).tupled(args)) diff --git a/test/v1/controllers/requestParsers/AmendDisclosuresRequestParserSpec.scala b/test/v1/controllers/requestParsers/AmendDisclosuresRequestParserSpec.scala index 37e7f12..ad7908c 100644 --- a/test/v1/controllers/requestParsers/AmendDisclosuresRequestParserSpec.scala +++ b/test/v1/controllers/requestParsers/AmendDisclosuresRequestParserSpec.scala @@ -24,6 +24,7 @@ import v1.mocks.validators.MockAmendDisclosuresValidator import v1.models.errors._ import v1.models.request.disclosures._ + class AmendDisclosuresRequestParserSpec extends UnitSpec { val nino: String = "AA123456B" @@ -85,7 +86,7 @@ class AmendDisclosuresRequestParserSpec extends UnitSpec { "parse" should { "return a request object" when { "valid request data is supplied" in new Test { - MockAmendDisclosuresValidator.validate(amendDisclosuresRawData).returns(Nil) + MockAmendDisclosuresValidator.validate(amendDisclosuresRawData).returns(List.empty[MtdError]) parser.parseRequest(amendDisclosuresRawData) shouldBe Right(AmendDisclosuresRequest(Nino(nino), taxYear, validRequestBodyModel)) @@ -106,7 +107,7 @@ class AmendDisclosuresRequestParserSpec extends UnitSpec { .returns(List(NinoFormatError, TaxYearFormatError)) parser.parseRequest(amendDisclosuresRawData.copy(nino = "notANino", taxYear = "notATaxYear")) shouldBe - Left(ErrorWrapper(correlationId, BadRequestError, Some(Seq(NinoFormatError, TaxYearFormatError)))) + Left(ErrorWrapper(correlationId, BadRequestError, Some(List(NinoFormatError, TaxYearFormatError)))) } "path parameter TaxYearNotSupported validation occurs" in new Test { diff --git a/test/v1/controllers/requestParsers/CreateMarriageAllowanceRequestParserSpec.scala b/test/v1/controllers/requestParsers/CreateMarriageAllowanceRequestParserSpec.scala index eb23118..b5268d3 100644 --- a/test/v1/controllers/requestParsers/CreateMarriageAllowanceRequestParserSpec.scala +++ b/test/v1/controllers/requestParsers/CreateMarriageAllowanceRequestParserSpec.scala @@ -21,9 +21,10 @@ import play.api.mvc.AnyContentAsJson import support.UnitSpec import v1.mocks.validators.MockCreateMarriageAllowanceValidator import v1.models.domain.Nino -import v1.models.errors.{BadRequestError, ErrorWrapper, NinoFormatError, RuleIncorrectOrEmptyBodyError} +import v1.models.errors.{BadRequestError, ErrorWrapper, MtdError, NinoFormatError, RuleIncorrectOrEmptyBodyError} import v1.models.request.marriageAllowance.{CreateMarriageAllowanceBody, CreateMarriageAllowanceRawData, CreateMarriageAllowanceRequest} + class CreateMarriageAllowanceRequestParserSpec extends UnitSpec { implicit val correlationId: String = "a1e8057e-fbbc-47a8-a8b4-78d9f015c253" @@ -48,7 +49,7 @@ class CreateMarriageAllowanceRequestParserSpec extends UnitSpec { |} |""".stripMargin))) - MockCreateMarriageAllowanceValidator.validate(rawData) returns Nil + MockCreateMarriageAllowanceValidator.validate(rawData) returns List.empty[MtdError] parser.parseRequest(rawData) shouldBe Right(CreateMarriageAllowanceRequest(Nino(nino), CreateMarriageAllowanceBody( spouseOrCivilPartnerNino = "AA123456B", @@ -79,7 +80,7 @@ class CreateMarriageAllowanceRequestParserSpec extends UnitSpec { "return a ErrorWrapper for a BadRequestError with the errors" in new Test { val rawData: CreateMarriageAllowanceRawData = CreateMarriageAllowanceRawData(ignoredNino, ignoredBody) - val errors = List(NinoFormatError, RuleIncorrectOrEmptyBodyError) + val errors: List[MtdError] = List(NinoFormatError, RuleIncorrectOrEmptyBodyError) MockCreateMarriageAllowanceValidator.validate(rawData) returns errors parser.parseRequest(rawData) shouldBe Left(ErrorWrapper(correlationId, BadRequestError, Some(errors))) diff --git a/test/v1/controllers/requestParsers/DeleteRetrieveRequestParserSpec.scala b/test/v1/controllers/requestParsers/DeleteRetrieveRequestParserSpec.scala index 14c3a8d..20a9d91 100644 --- a/test/v1/controllers/requestParsers/DeleteRetrieveRequestParserSpec.scala +++ b/test/v1/controllers/requestParsers/DeleteRetrieveRequestParserSpec.scala @@ -22,6 +22,7 @@ import v1.mocks.validators.MockDeleteRetrieveValidator import v1.models.errors._ import v1.models.request.{DeleteRetrieveRawData, DeleteRetrieveRequest} + class DeleteRetrieveRequestParserSpec extends UnitSpec { val nino: String = "AA123456B" @@ -42,7 +43,7 @@ class DeleteRetrieveRequestParserSpec extends UnitSpec { "parse" should { "return a request object" when { "valid request data is supplied" in new Test { - MockDeleteRetrieveValidator.validate(deleteRetrieveDisclosuresRawData).returns(Nil) + MockDeleteRetrieveValidator.validate(deleteRetrieveDisclosuresRawData).returns(List.empty[MtdError]) parser.parseRequest(deleteRetrieveDisclosuresRawData) shouldBe Right(DeleteRetrieveRequest(Nino(nino), taxYear)) @@ -71,7 +72,7 @@ class DeleteRetrieveRequestParserSpec extends UnitSpec { .returns(List(NinoFormatError, TaxYearFormatError)) parser.parseRequest(deleteRetrieveDisclosuresRawData) shouldBe - Left(ErrorWrapper(correlationId, BadRequestError, Some(Seq(NinoFormatError, TaxYearFormatError)))) + Left(ErrorWrapper(correlationId, BadRequestError, Some(List(NinoFormatError, TaxYearFormatError)))) } } } diff --git a/test/v1/controllers/requestParsers/RequestParserSpec.scala b/test/v1/controllers/requestParsers/RequestParserSpec.scala index 69f627f..31b50bd 100644 --- a/test/v1/controllers/requestParsers/RequestParserSpec.scala +++ b/test/v1/controllers/requestParsers/RequestParserSpec.scala @@ -17,13 +17,13 @@ package v1.controllers.requestParsers import support.UnitSpec -import v1.models.domain.Nino import v1.controllers.requestParsers.validators.Validator -import v1.models.errors.{BadRequestError, ErrorWrapper, NinoFormatError, RuleIncorrectOrEmptyBodyError} +import v1.models.domain.Nino +import v1.models.errors._ import v1.models.request.RawData -class RequestParserSpec extends UnitSpec { +class RequestParserSpec extends UnitSpec { private val nino = "AA123456A" implicit val correlationId: String = "a1e8057e-fbbc-47a8-a8b4-78d9f015c253" @@ -45,7 +45,7 @@ class RequestParserSpec extends UnitSpec { "parse" should { "return a Request" when { "the validator returns no errors" in new Test { - lazy val validator: Validator[Raw] = (_: Raw) => Nil + lazy val validator: Validator[Raw] = (_: Raw) => List.empty[MtdError] parser.parseRequest(Raw(nino)) shouldBe Right(Request(Nino(nino))) } @@ -63,9 +63,9 @@ class RequestParserSpec extends UnitSpec { "the validator returns multiple errors" in new Test { lazy val validator: Validator[Raw] = (_: Raw) => List(NinoFormatError, RuleIncorrectOrEmptyBodyError) - parser.parseRequest(Raw(nino)) shouldBe Left(ErrorWrapper(correlationId, BadRequestError, Some(Seq(NinoFormatError, RuleIncorrectOrEmptyBodyError)))) + parser.parseRequest(Raw(nino)) shouldBe + Left(ErrorWrapper(correlationId, BadRequestError, Some(List(NinoFormatError, RuleIncorrectOrEmptyBodyError)))) } } } - -} \ No newline at end of file +} diff --git a/test/v1/controllers/requestParsers/validators/AmendDisclosuresValidatorSpec.scala b/test/v1/controllers/requestParsers/validators/AmendDisclosuresValidatorSpec.scala index 5d375a9..dc4e75a 100644 --- a/test/v1/controllers/requestParsers/validators/AmendDisclosuresValidatorSpec.scala +++ b/test/v1/controllers/requestParsers/validators/AmendDisclosuresValidatorSpec.scala @@ -24,8 +24,8 @@ import support.UnitSpec import v1.models.errors._ import v1.models.request.disclosures.AmendDisclosuresRawData -class AmendDisclosuresValidatorSpec extends UnitSpec with MockAppConfig { +class AmendDisclosuresValidatorSpec extends UnitSpec with MockAppConfig { private val validNino = "AA123456A" private val validTaxYear = "2021-22" @@ -155,9 +155,7 @@ class AmendDisclosuresValidatorSpec extends UnitSpec with MockAppConfig { private val allInvalidValueRawRequestBody = AnyContentAsJson(allInvalidValueRequestBodyJson) class Test extends MockAppConfig { - implicit val appConfig: AppConfig = mockAppConfig - val validator = new AmendDisclosuresValidator() MockAppConfig.minimumPermittedTaxYear @@ -168,7 +166,8 @@ class AmendDisclosuresValidatorSpec extends UnitSpec with MockAppConfig { "running a validation" should { "return no errors" when { "a valid request is supplied" in new Test { - validator.validate(AmendDisclosuresRawData(validNino, validTaxYear, validRawRequestBody)) shouldBe Nil + validator.validate(AmendDisclosuresRawData(validNino, validTaxYear, validRawRequestBody)) shouldBe + List.empty[MtdError] } } @@ -223,7 +222,9 @@ class AmendDisclosuresValidatorSpec extends UnitSpec with MockAppConfig { "the submitted request body is not in the correct format" in new Test { validator.validate(AmendDisclosuresRawData(validNino, validTaxYear, nonValidRawRequestBody)) shouldBe - List(RuleIncorrectOrEmptyBodyError.copy(paths = Some(Seq("/taxAvoidance/0/srn", "/class2Nics/class2VoluntaryContributions")))) + List(RuleIncorrectOrEmptyBodyError.copy( + paths = Some(List("/taxAvoidance/0/srn", "/class2Nics/class2VoluntaryContributions"))) + ) } } @@ -260,27 +261,10 @@ class AmendDisclosuresValidatorSpec extends UnitSpec with MockAppConfig { "multiple fields fail value validation" in new Test { validator.validate(AmendDisclosuresRawData(validNino, validTaxYear, allInvalidValueRawRequestBody)) shouldBe List( - SRNFormatError.copy( - paths = Some(List( - "/taxAvoidance/0/srn", - "/taxAvoidance/1/srn" - )) - ), - TaxYearFormatError.copy( - paths = Some(List( - "/taxAvoidance/0/taxYear" - )) - ), - RuleTaxYearRangeInvalidError.copy( - paths = Some(List( - "/taxAvoidance/1/taxYear" - )) - ), - RuleVoluntaryClass2ValueInvalidError.copy( - paths = Some(List( - "/class2Nics/class2VoluntaryContributions" - )) - ), + SRNFormatError.copy(paths = Some(List("/taxAvoidance/0/srn", "/taxAvoidance/1/srn"))), + TaxYearFormatError.copy(paths = Some(List("/taxAvoidance/0/taxYear"))), + RuleTaxYearRangeInvalidError.copy(paths = Some(List("/taxAvoidance/1/taxYear"))), + RuleVoluntaryClass2ValueInvalidError.copy(paths = Some(List("/class2Nics/class2VoluntaryContributions"))), ) } } diff --git a/test/v1/controllers/requestParsers/validators/CreateMarriageAllowanceValidatorSpec.scala b/test/v1/controllers/requestParsers/validators/CreateMarriageAllowanceValidatorSpec.scala index 743f371..e4bd975 100644 --- a/test/v1/controllers/requestParsers/validators/CreateMarriageAllowanceValidatorSpec.scala +++ b/test/v1/controllers/requestParsers/validators/CreateMarriageAllowanceValidatorSpec.scala @@ -22,10 +22,9 @@ import support.UnitSpec import v1.models.errors._ import v1.models.request.marriageAllowance.CreateMarriageAllowanceRawData -class CreateMarriageAllowanceValidatorSpec extends UnitSpec { +class CreateMarriageAllowanceValidatorSpec extends UnitSpec { val validator = new CreateMarriageAllowanceValidator - val nino = "AA123456A" val body: AnyContentAsJson = AnyContentAsJson(Json.parse("""{ @@ -39,7 +38,7 @@ class CreateMarriageAllowanceValidatorSpec extends UnitSpec { "CreateMarriageAllowanceValidator" when { "valid request" must { "return no errors" in { - validator.validate(CreateMarriageAllowanceRawData(nino, body)) shouldBe Nil + validator.validate(CreateMarriageAllowanceRawData(nino, body)) shouldBe List.empty[MtdError] } } @@ -61,7 +60,7 @@ class CreateMarriageAllowanceValidatorSpec extends UnitSpec { |} |""".stripMargin)) - validator.validate(CreateMarriageAllowanceRawData(nino, badBody)) shouldBe Nil + validator.validate(CreateMarriageAllowanceRawData(nino, badBody)) shouldBe List.empty[MtdError] } } @@ -106,7 +105,7 @@ class CreateMarriageAllowanceValidatorSpec extends UnitSpec { |""".stripMargin)) validator.validate(CreateMarriageAllowanceRawData(nino, bodyWithNoSurname)) shouldBe List( - RuleIncorrectOrEmptyBodyError.copy(paths = Some(Seq("/spouseOrCivilPartnerSurname")))) + RuleIncorrectOrEmptyBodyError.copy(paths = Some(List("/spouseOrCivilPartnerSurname")))) } } @@ -150,11 +149,13 @@ class CreateMarriageAllowanceValidatorSpec extends UnitSpec { |} |""".stripMargin)) - validator.validate(CreateMarriageAllowanceRawData(nino, badBody)) should contain allOf ( - PartnerNinoFormatError, - PartnerFirstNameFormatError, - PartnerSurnameFormatError, - PartnerDoBFormatError, + validator.validate(CreateMarriageAllowanceRawData(nino, badBody)) should contain.allElementsOf( + List( + PartnerNinoFormatError, + PartnerFirstNameFormatError, + PartnerSurnameFormatError, + PartnerDoBFormatError, + ) ) } } diff --git a/test/v1/controllers/requestParsers/validators/DeleteRetrieveValidatorSpec.scala b/test/v1/controllers/requestParsers/validators/DeleteRetrieveValidatorSpec.scala index 9e3c524..f3416dc 100644 --- a/test/v1/controllers/requestParsers/validators/DeleteRetrieveValidatorSpec.scala +++ b/test/v1/controllers/requestParsers/validators/DeleteRetrieveValidatorSpec.scala @@ -22,15 +22,13 @@ import support.UnitSpec import v1.models.errors._ import v1.models.request.DeleteRetrieveRawData -class DeleteRetrieveValidatorSpec extends UnitSpec with MockAppConfig { +class DeleteRetrieveValidatorSpec extends UnitSpec with MockAppConfig { private val validNino = "AA123456A" private val validTaxYear = "2021-22" class Test extends MockAppConfig { - implicit val appConfig: AppConfig = mockAppConfig - val validator = new DeleteRetrieveValidator() MockAppConfig.minimumPermittedTaxYear @@ -41,22 +39,20 @@ class DeleteRetrieveValidatorSpec extends UnitSpec with MockAppConfig { "running a validation" should { "return no errors" when { "a valid request is supplied" in new Test { - validator.validate(DeleteRetrieveRawData(validNino, validTaxYear)) shouldBe Nil + validator.validate(DeleteRetrieveRawData(validNino, validTaxYear)) shouldBe List.empty[MtdError] } } // parameter format error scenarios "return NinoFormatError error" when { "an invalid nino is supplied" in new Test { - validator.validate(DeleteRetrieveRawData("A12344A", validTaxYear)) shouldBe - List(NinoFormatError) + validator.validate(DeleteRetrieveRawData("A12344A", validTaxYear)) shouldBe List(NinoFormatError) } } "return TaxYearFormatError error" when { "an invalid tax year is supplied" in new Test { - validator.validate(DeleteRetrieveRawData(validNino, "20178")) shouldBe - List(TaxYearFormatError) + validator.validate(DeleteRetrieveRawData(validNino, "20178")) shouldBe List(TaxYearFormatError) } } @@ -70,15 +66,13 @@ class DeleteRetrieveValidatorSpec extends UnitSpec with MockAppConfig { // parameter rule error scenarios "return RuleTaxYearNotSupportedError error" when { "an unsupported tax year is supplied" in new Test { - validator.validate(DeleteRetrieveRawData(validNino, "2020-21")) shouldBe - List(RuleTaxYearNotSupportedError) + validator.validate(DeleteRetrieveRawData(validNino, "2020-21")) shouldBe List(RuleTaxYearNotSupportedError) } } "return RuleTaxYearRangeInvalidError error" when { "an invalid tax year range is supplied" in new Test { - validator.validate(DeleteRetrieveRawData(validNino, "2019-21")) shouldBe - List(RuleTaxYearRangeInvalidError) + validator.validate(DeleteRetrieveRawData(validNino, "2019-21")) shouldBe List(RuleTaxYearRangeInvalidError) } } } diff --git a/test/v1/controllers/requestParsers/validators/ValidatorSpec.scala b/test/v1/controllers/requestParsers/validators/ValidatorSpec.scala index 292a514..c0b7b02 100644 --- a/test/v1/controllers/requestParsers/validators/ValidatorSpec.scala +++ b/test/v1/controllers/requestParsers/validators/ValidatorSpec.scala @@ -21,6 +21,7 @@ import support.UnitSpec import v1.models.errors._ import v1.models.request.RawData + class ValidatorSpec extends UnitSpec with MockFactory { private trait Test { @@ -30,7 +31,6 @@ class ValidatorSpec extends UnitSpec with MockFactory { "running a validation" should { "return no errors" when { "when all data is correct " in new Test { - // Set up the mock validations val levelOneValidationOne = new MockFunctionObject("Level: 1 Validation 1") val levelOneValidationTwo = new MockFunctionObject("Level: 1 Validation 2") @@ -42,7 +42,7 @@ class ValidatorSpec extends UnitSpec with MockFactory { ) } - val validationSet = List(levelOneValidations) + val validationSet: List[TestRawData => List[List[MtdError]]] = List(levelOneValidations) val inputData: TestRawData = TestRawData("ABCDEF", "12345") val result: List[MtdError] = validator.run(validationSet, inputData) @@ -67,7 +67,7 @@ class ValidatorSpec extends UnitSpec with MockFactory { ) } - val validationSet = List(levelOneValidations) + val validationSet: List[TestRawData => List[List[MtdError]]] = List(levelOneValidations) val inputData: TestRawData = TestRawData("ABCDEF", "12345") val result: List[MtdError] = validator.run(validationSet, inputData) @@ -102,7 +102,7 @@ class ValidatorSpec extends UnitSpec with MockFactory { ) } - val validationSet = List(levelOneValidations, levelTwoValidations) + val validationSet: List[TestRawData => List[List[MtdError]]] = List(levelOneValidations, levelTwoValidations) val inputData: TestRawData = TestRawData("ABCDEF", "12345") val result: List[MtdError] = validator.run(validationSet, inputData) @@ -121,13 +121,13 @@ class ValidatorSpec extends UnitSpec with MockFactory { "combine errors of the same type" in { val errors: List[List[MtdError]] = List( List(NotFoundError), - List(NinoFormatError.copy(paths = Some(Seq("one")))), - List(NinoFormatError.copy(paths = Some(Seq("two")))) + List(NinoFormatError.copy(paths = Some(List("one")))), + List(NinoFormatError.copy(paths = Some(List("two")))) ) val flatErrors: List[MtdError] = List( NotFoundError, - NinoFormatError.copy(paths = Some(Seq("one", "two"))) + NinoFormatError.copy(paths = Some(List("one", "two"))) ) Validator.flattenErrors(errors) shouldBe flatErrors @@ -136,7 +136,7 @@ class ValidatorSpec extends UnitSpec with MockFactory { "return the input for a list of unique errors" in { val errors: List[List[MtdError]] = List( List(NotFoundError), - List(NinoFormatError.copy(paths = Some(Seq("one")))) + List(NinoFormatError.copy(paths = Some(List("one")))) ) Validator.flattenErrors(errors) shouldBe errors.flatten @@ -166,11 +166,13 @@ private case class TestRawData(fieldOne: String, fieldTwo: String) extends RawDa // Create a Validator based off the trait to be able to test it private class TestValidator extends Validator[TestRawData] { + val ListNil: List[MtdError] = List.empty[MtdError] + override def validate(data: TestRawData): List[MtdError] = { - run(List(), data) match { - case Nil => List() - case err :: Nil => List(err) - case errs => errs + run(List.empty[ValidationType], data) match { + case `ListNil` => List.empty[MtdError] + case errs if errs.nonEmpty => List(errs.head) + case errs => errs } } } diff --git a/test/v1/controllers/requestParsers/validators/validations/DateFormatValidationSpec.scala b/test/v1/controllers/requestParsers/validators/validations/DateFormatValidationSpec.scala index 4cec472..3ac2950 100644 --- a/test/v1/controllers/requestParsers/validators/validations/DateFormatValidationSpec.scala +++ b/test/v1/controllers/requestParsers/validators/validations/DateFormatValidationSpec.scala @@ -19,10 +19,11 @@ package v1.controllers.requestParsers.validators.validations import support.UnitSpec import v1.models.errors.MtdError + class DateFormatValidationSpec extends UnitSpec { "DateFormatValidation" when { - object DummyError extends MtdError("ERROR_CODE", "Error message", Some(Seq("/path"))) + object DummyError extends MtdError(code = "ERROR_CODE", message = "Error message", paths = Some(List("/path"))) "validate" must { "return an empty list for a valid date" in { diff --git a/test/v1/controllers/requestParsers/validators/validations/JsonFormatValidationSpec.scala b/test/v1/controllers/requestParsers/validators/validations/JsonFormatValidationSpec.scala index cce13d7..e588859 100644 --- a/test/v1/controllers/requestParsers/validators/validations/JsonFormatValidationSpec.scala +++ b/test/v1/controllers/requestParsers/validators/validations/JsonFormatValidationSpec.scala @@ -21,10 +21,10 @@ import support.UnitSpec import v1.models.errors.RuleIncorrectOrEmptyBodyError import v1.models.utils.JsonErrorValidators + class JsonFormatValidationSpec extends UnitSpec with JsonErrorValidators { case class TestDataObject(fieldOne: String, fieldTwo: String) - case class TestDataWrapper(arrayField: Seq[TestDataObject]) implicit val testDataObjectFormat: OFormat[TestDataObject] = Json.format[TestDataObject] @@ -33,72 +33,75 @@ class JsonFormatValidationSpec extends UnitSpec with JsonErrorValidators { "validate" should { "return no errors" when { "when a valid JSON object with all the necessary fields is supplied" in { - val validJson = Json.parse("""{ "fieldOne" : "Something", "fieldTwo" : "SomethingElse" }""") - val validationResult = JsonFormatValidation.validate[TestDataObject](validJson) + validationResult shouldBe empty } } "return an error " when { "required field is missing" in { - // fieldTwo is missing val json = Json.parse("""{ "fieldOne" : "Something" }""") - val validationResult = JsonFormatValidation.validate[TestDataObject](json) - validationResult shouldBe List(RuleIncorrectOrEmptyBodyError.copy(paths = Some(Seq("/fieldTwo")))) + + validationResult shouldBe List(RuleIncorrectOrEmptyBodyError.copy(paths = Some(List("/fieldTwo")))) } "required field is missing in array object" in { - // both fields are missing val json = Json.parse("""{ "arrayField" : [{}]}""") - val validationResult = JsonFormatValidation.validate[TestDataWrapper](json) - validationResult shouldBe List(RuleIncorrectOrEmptyBodyError.copy(paths = Some(Seq("/arrayField/0/fieldOne", "/arrayField/0/fieldTwo")))) + + val error = validationResult.head + error.message shouldBe RuleIncorrectOrEmptyBodyError.message + error.code shouldBe RuleIncorrectOrEmptyBodyError.code + error.paths.get should contain.allElementsOf(List("/arrayField/0/fieldOne", "/arrayField/0/fieldTwo")) } "required field is missing in multiple array objects" in { - // both fields are missing val json = Json.parse("""{ "arrayField" : [{}, {}]}""") - val validationResult = JsonFormatValidation.validate[TestDataWrapper](json) - validationResult shouldBe List(RuleIncorrectOrEmptyBodyError.copy(paths = - Some(Seq( - "/arrayField/0/fieldOne", - "/arrayField/0/fieldTwo", - "/arrayField/1/fieldOne", - "/arrayField/1/fieldTwo" - )) + + val error = validationResult.head + error.message shouldBe RuleIncorrectOrEmptyBodyError.message + error.code shouldBe RuleIncorrectOrEmptyBodyError.code + error.paths.get should contain.allElementsOf(List( + "/arrayField/0/fieldOne", + "/arrayField/0/fieldTwo", + "/arrayField/1/fieldOne", + "/arrayField/1/fieldTwo" )) } "empty body is submitted" in { - val json = Json.parse("""{}""") - val validationResult = JsonFormatValidation.validate[TestDataObject](json) + validationResult shouldBe List(RuleIncorrectOrEmptyBodyError) } "a non-empty body is supplied without any expected fields" in { - val json = Json.parse("""{"field": "value"}""") - val validationResult = JsonFormatValidation.validate[TestDataObject](json) - validationResult shouldBe List(RuleIncorrectOrEmptyBodyError.copy(paths = Some(Seq("/fieldOne", "/fieldTwo")))) + + val error = validationResult.head + error.message shouldBe RuleIncorrectOrEmptyBodyError.message + error.code shouldBe RuleIncorrectOrEmptyBodyError.code + error.paths.get should contain.allElementsOf(List( + "/fieldOne", + "/fieldTwo", + )) } "a field is supplied with the wrong data type" in { - val json = Json.parse("""{"fieldOne": true, "fieldTwo": "value"}""") - val validationResult = JsonFormatValidation.validate[TestDataObject](json) - validationResult shouldBe List(RuleIncorrectOrEmptyBodyError.copy(paths = Some(Seq("/fieldOne")))) + + validationResult shouldBe List(RuleIncorrectOrEmptyBodyError.copy(paths = Some(List("/fieldOne")))) } } } -} \ No newline at end of file +} diff --git a/test/v1/controllers/requestParsers/validators/validations/RegexValidationSpec.scala b/test/v1/controllers/requestParsers/validators/validations/RegexValidationSpec.scala index 9b2122c..67f7f9b 100644 --- a/test/v1/controllers/requestParsers/validators/validations/RegexValidationSpec.scala +++ b/test/v1/controllers/requestParsers/validators/validations/RegexValidationSpec.scala @@ -28,7 +28,7 @@ class RegexValidationSpec extends UnitSpec { } "RegexValidation" when { - object DummyError extends MtdError("ERROR_CODE", "Error message", Some(Seq("/path"))) + object DummyError extends MtdError(code = "ERROR_CODE", message = "Error message", paths = Some(List("/path"))) "matches regex" must { "return an empty list" in { diff --git a/test/v1/hateoas/HateoasFactorySpec.scala b/test/v1/hateoas/HateoasFactorySpec.scala index 74b2004..b9edaa7 100644 --- a/test/v1/hateoas/HateoasFactorySpec.scala +++ b/test/v1/hateoas/HateoasFactorySpec.scala @@ -36,7 +36,7 @@ class HateoasFactorySpec extends UnitSpec with MockAppConfig { val response: Response = Response("X") class Test { - MockAppConfig.apiGatewayContext.returns("context").anyNumberOfTimes + MockAppConfig.apiGatewayContext.returns("context").anyNumberOfTimes() } "wrap" should { diff --git a/test/v1/mocks/MockIdGenerator.scala b/test/v1/mocks/MockIdGenerator.scala index 1c84fc1..62c098c 100644 --- a/test/v1/mocks/MockIdGenerator.scala +++ b/test/v1/mocks/MockIdGenerator.scala @@ -20,12 +20,10 @@ import org.scalamock.handlers.CallHandler import org.scalamock.scalatest.MockFactory import utils.IdGenerator - trait MockIdGenerator extends MockFactory { - val mockIdGenerator: IdGenerator = mock[IdGenerator] object MockIdGenerator { - def generateCorrelationId: CallHandler[String] = (mockIdGenerator.generateCorrelationId _).expects() + def generateCorrelationId: CallHandler[String] = (() => mockIdGenerator.generateCorrelationId: String).expects() } -} \ No newline at end of file +} diff --git a/test/v1/mocks/hateoas/MockHateoasFactory.scala b/test/v1/mocks/hateoas/MockHateoasFactory.scala index ab2e75a..32e3fc1 100644 --- a/test/v1/mocks/hateoas/MockHateoasFactory.scala +++ b/test/v1/mocks/hateoas/MockHateoasFactory.scala @@ -22,8 +22,6 @@ import org.scalamock.scalatest.MockFactory import v1.hateoas.{HateoasFactory, HateoasLinksFactory, HateoasListLinksFactory} import v1.models.hateoas.{HateoasData, HateoasWrapper} -import scala.language.higherKinds - trait MockHateoasFactory extends MockFactory { val mockHateoasFactory: HateoasFactory = mock[HateoasFactory] diff --git a/test/v1/mocks/services/MockDeleteRetrieveService.scala b/test/v1/mocks/services/MockDeleteRetrieveService.scala index 7e6be4f..5c61c9b 100644 --- a/test/v1/mocks/services/MockDeleteRetrieveService.scala +++ b/test/v1/mocks/services/MockDeleteRetrieveService.scala @@ -34,7 +34,7 @@ trait MockDeleteRetrieveService extends MockFactory { object MockDeleteRetrieveService { - val defaultDesMap: Map[String, MtdError] = Map.empty[String, MtdError] + val defaultdownstreamMap: Map[String, MtdError] = Map.empty[String, MtdError] def delete(): CallHandler[Future[Either[ErrorWrapper, ResponseWrapper[Unit]]]] = { (mockDeleteRetrieveService diff --git a/test/v1/mocks/validators/MockAmendDisclosuresValidator.scala b/test/v1/mocks/validators/MockAmendDisclosuresValidator.scala index 79d8eae..46b1c51 100644 --- a/test/v1/mocks/validators/MockAmendDisclosuresValidator.scala +++ b/test/v1/mocks/validators/MockAmendDisclosuresValidator.scala @@ -22,8 +22,8 @@ import v1.controllers.requestParsers.validators.AmendDisclosuresValidator import v1.models.errors.MtdError import v1.models.request.disclosures.AmendDisclosuresRawData -class MockAmendDisclosuresValidator extends MockFactory { +class MockAmendDisclosuresValidator extends MockFactory { val mockAmendDisclosuresValidator: AmendDisclosuresValidator = mock[AmendDisclosuresValidator] object MockAmendDisclosuresValidator { @@ -34,5 +34,4 @@ class MockAmendDisclosuresValidator extends MockFactory { .expects(data) } } - -} \ No newline at end of file +} diff --git a/test/v1/mocks/validators/MockCreateMarriageAllowanceValidator.scala b/test/v1/mocks/validators/MockCreateMarriageAllowanceValidator.scala index d22c9fb..9b9f5e7 100644 --- a/test/v1/mocks/validators/MockCreateMarriageAllowanceValidator.scala +++ b/test/v1/mocks/validators/MockCreateMarriageAllowanceValidator.scala @@ -22,13 +22,14 @@ import v1.controllers.requestParsers.validators.CreateMarriageAllowanceValidator import v1.models.errors.MtdError import v1.models.request.marriageAllowance.CreateMarriageAllowanceRawData -class MockCreateMarriageAllowanceValidator extends MockFactory { +class MockCreateMarriageAllowanceValidator extends MockFactory { val mockCreateMarriageAllowanceValidator: CreateMarriageAllowanceValidator = mock[CreateMarriageAllowanceValidator] object MockCreateMarriageAllowanceValidator { def validate(data: CreateMarriageAllowanceRawData): CallHandler1[CreateMarriageAllowanceRawData, List[MtdError]] = - (mockCreateMarriageAllowanceValidator.validate(_: CreateMarriageAllowanceRawData)).expects(data) + (mockCreateMarriageAllowanceValidator + .validate(_: CreateMarriageAllowanceRawData)) + .expects(data) } - -} \ No newline at end of file +} diff --git a/test/v1/mocks/validators/MockDeleteRetrieveValidator.scala b/test/v1/mocks/validators/MockDeleteRetrieveValidator.scala index cef28db..a0abd9f 100644 --- a/test/v1/mocks/validators/MockDeleteRetrieveValidator.scala +++ b/test/v1/mocks/validators/MockDeleteRetrieveValidator.scala @@ -22,8 +22,8 @@ import v1.controllers.requestParsers.validators.DeleteRetrieveValidator import v1.models.errors.MtdError import v1.models.request.DeleteRetrieveRawData -class MockDeleteRetrieveValidator extends MockFactory { +class MockDeleteRetrieveValidator extends MockFactory { val mockDeleteRetrieveValidator: DeleteRetrieveValidator = mock[DeleteRetrieveValidator] object MockDeleteRetrieveValidator { diff --git a/test/v1/models/audit/AuditResponseSpec.scala b/test/v1/models/audit/AuditResponseSpec.scala index 4880369..dfdfb6f 100644 --- a/test/v1/models/audit/AuditResponseSpec.scala +++ b/test/v1/models/audit/AuditResponseSpec.scala @@ -20,9 +20,10 @@ import play.api.http.Status.{BAD_REQUEST, OK} import play.api.libs.json.{JsValue, Json} import support.UnitSpec + class AuditResponseSpec extends UnitSpec { - val auditErrors: Seq[AuditError] = Seq(AuditError(errorCode = "FORMAT_NINO"), AuditError(errorCode = "FORMAT_TAX_YEAR")) + val auditErrors: List[AuditError] = List(AuditError(errorCode = "FORMAT_NINO"), AuditError(errorCode = "FORMAT_TAX_YEAR")) val body: JsValue = Json.parse("""{ "aField" : "aValue" }""") val auditResponseModelWithBody: AuditResponse = diff --git a/test/v1/models/audit/GenericAuditDetailSpec.scala b/test/v1/models/audit/GenericAuditDetailSpec.scala index 6525f28..c688264 100644 --- a/test/v1/models/audit/GenericAuditDetailSpec.scala +++ b/test/v1/models/audit/GenericAuditDetailSpec.scala @@ -21,6 +21,7 @@ import play.api.libs.json.{JsValue, Json} import support.UnitSpec import v1.models.errors.TaxYearFormatError + class GenericAuditDetailSpec extends UnitSpec { val nino: String = "XX751130C" @@ -173,7 +174,7 @@ class GenericAuditDetailSpec extends UnitSpec { """.stripMargin )), `X-CorrelationId` = correlationId, - response = AuditResponse(BAD_REQUEST, Left(Seq(AuditError(TaxYearFormatError.code)))) + response = AuditResponse(BAD_REQUEST, Left(List(AuditError(TaxYearFormatError.code)))) ) "GenericAuditDetail" when { diff --git a/test/v1/models/domain/DesTaxYearSpec.scala b/test/v1/models/domain/DownstreamTaxYearSpec.scala similarity index 75% rename from test/v1/models/domain/DesTaxYearSpec.scala rename to test/v1/models/domain/DownstreamTaxYearSpec.scala index 47f67af..41fb54a 100644 --- a/test/v1/models/domain/DesTaxYearSpec.scala +++ b/test/v1/models/domain/DownstreamTaxYearSpec.scala @@ -18,16 +18,16 @@ package v1.models.domain import support.UnitSpec -class DesTaxYearSpec extends UnitSpec { +class DownstreamTaxYearSpec extends UnitSpec { "toString" should { "return the value inside the model as a String instead of the standard case class toString" in { - DesTaxYear("value").toString shouldBe "value" + DownstreamTaxYear("value").toString shouldBe "value" } } "fromMtd" should { - "return the DES representation of an MTD tax year (XXYY-ZZ -> XXZZ)" in { - DesTaxYear.fromMtd("2018-19") shouldBe DesTaxYear("2019") + "return the downstream representation of an MTD tax year (XXYY-ZZ -> XXZZ)" in { + DownstreamTaxYear.fromMtd("2018-19") shouldBe DownstreamTaxYear("2019") } } } diff --git a/test/v1/models/errors/DesErrorCodeSpec.scala b/test/v1/models/errors/DownstreamErrorCodeSpec.scala similarity index 88% rename from test/v1/models/errors/DesErrorCodeSpec.scala rename to test/v1/models/errors/DownstreamErrorCodeSpec.scala index a6f5a97..87c15b0 100644 --- a/test/v1/models/errors/DesErrorCodeSpec.scala +++ b/test/v1/models/errors/DownstreamErrorCodeSpec.scala @@ -19,7 +19,7 @@ package v1.models.errors import play.api.libs.json.Json import support.UnitSpec -class DesErrorCodeSpec extends UnitSpec { +class DownstreamErrorCodeSpec extends UnitSpec { "reads" should { val json = Json.parse( @@ -32,7 +32,7 @@ class DesErrorCodeSpec extends UnitSpec { ) "generate the correct error code" in { - json.as[DesErrorCode] shouldBe DesErrorCode("CODE") + json.as[DownstreamErrorCode] shouldBe DownstreamErrorCode("CODE") } } } diff --git a/test/v1/models/errors/ErrorWrapperSpec.scala b/test/v1/models/errors/ErrorWrapperSpec.scala index c09f484..b8514c1 100644 --- a/test/v1/models/errors/ErrorWrapperSpec.scala +++ b/test/v1/models/errors/ErrorWrapperSpec.scala @@ -20,12 +20,12 @@ import play.api.libs.json.Json import support.UnitSpec import v1.models.audit.AuditError -class ErrorWrapperSpec extends UnitSpec { +class ErrorWrapperSpec extends UnitSpec { val correlationId: String = "X-123" "Rendering a error response with one error" should { - val error = ErrorWrapper(correlationId, NinoFormatError, Some(Seq.empty)) + val error = ErrorWrapper(correlationId, NinoFormatError, Some(List.empty[MtdError])) val json = Json.parse( """ @@ -42,7 +42,7 @@ class ErrorWrapperSpec extends UnitSpec { } "Rendering a error response with one error and an empty sequence of errors" should { - val error = ErrorWrapper(correlationId, NinoFormatError, Some(Seq.empty)) + val error = ErrorWrapper(correlationId, NinoFormatError, Some(List.empty[MtdError])) val json = Json.parse( """ @@ -59,14 +59,7 @@ class ErrorWrapperSpec extends UnitSpec { } "Rendering a error response with two errors" should { - val error = ErrorWrapper(correlationId, BadRequestError, - Some ( - Seq( - NinoFormatError, - TaxYearFormatError - ) - ) - ) + val error = ErrorWrapper(correlationId, BadRequestError, Some(List(NinoFormatError, TaxYearFormatError))) val json = Json.parse( """ @@ -95,13 +88,12 @@ class ErrorWrapperSpec extends UnitSpec { "auditErrors" should { "map a single error to a single audit error" in { val singleWrappedError = ErrorWrapper(correlationId, NinoFormatError, None) - singleWrappedError.auditErrors shouldBe Seq(AuditError(NinoFormatError.code)) + singleWrappedError.auditErrors shouldBe List(AuditError(NinoFormatError.code)) } "map multiple errors to a sequence of audit errors" in { - val singleWrappedError = ErrorWrapper(correlationId, BadRequestError, Some(Seq(NinoFormatError, TaxYearFormatError))) - singleWrappedError.auditErrors shouldBe Seq(AuditError(NinoFormatError.code), AuditError(TaxYearFormatError.code)) + val singleWrappedError = ErrorWrapper(correlationId, BadRequestError, Some(List(NinoFormatError, TaxYearFormatError))) + singleWrappedError.auditErrors shouldBe List(AuditError(NinoFormatError.code), AuditError(TaxYearFormatError.code)) } } - -} \ No newline at end of file +} diff --git a/test/v1/models/utils/JsonErrorValidators.scala b/test/v1/models/utils/JsonErrorValidators.scala index 21375d5..a1829b6 100644 --- a/test/v1/models/utils/JsonErrorValidators.scala +++ b/test/v1/models/utils/JsonErrorValidators.scala @@ -22,8 +22,8 @@ import support.UnitSpec trait JsonErrorValidators { _: UnitSpec => - type JsError = (JsPath, Seq[JsonValidationError]) - type JsErrors = Seq[JsError] + type JsError = (JsPath, List[JsonValidationError]) + type JsErrors = List[JsError] object JsonError { val NUMBER_OR_STRING_FORMAT_EXCEPTION = "error.expected.jsnumberorjsstring" @@ -45,7 +45,7 @@ trait JsonErrorValidators { implicit class JsResultOps[T](res: JsResult[T]) { def errors: JsErrors = res match { - case JsError(jsErrors) => jsErrors + case JsError(jsErrors) => jsErrors.map(item => (item._1, item._2.toList)).toList case JsSuccess(_, _) => fail("A JSON error was expected") } } @@ -61,7 +61,6 @@ trait JsonErrorValidators { def testMandatoryProperty[A : Reads](json: JsValue)(property: String): Unit = { s"the JSON is missing the required property $property" should { - val jsPath: JsPath = property.split("/").filterNot(_ == "").foldLeft(JsPath())(_ \ _) val jsResult = json.removeProperty(jsPath).validate[A] @@ -70,7 +69,6 @@ trait JsonErrorValidators { } lazy val jsError = jsResult.errors.head - "throw the error against the correct property" in { jsError.path shouldBe jsPath } @@ -95,13 +93,11 @@ trait JsonErrorValidators { } s"the JSON has the wrong data type for path $path" should { - "only throw one error" in { jsResult.errors.size shouldBe 1 } lazy val jsError = jsResult.errors.head - "throw the error against the correct property" in { jsError.path shouldBe jsPath } @@ -117,11 +113,9 @@ trait JsonErrorValidators { json.as[JsObject](updateReads) } - private def filterErrorByPath(jsPath: JsPath, jsError: JsError): JsonValidationError = { - jsError match { - case (path, err :: Nil) if jsError.path == path => err - case (path, _ :: Nil)=> fail(s"single error returned but path $path does not match $jsPath") - case (path, errs @ _ :: _)=> fail(s"multiple errors returned for $path but only 1 required : $errs") - } + private def filterErrorByPath(jsPath: JsPath, jsError: JsError): JsonValidationError = jsError match { + case (path, err :: Nil) if jsError.path == path => err + case (path, _ :: Nil) => fail(s"single error returned but path $path does not match $jsPath") + case (path, errs) => fail(s"multiple errors returned for $path but only 1 required : $errs") } } diff --git a/test/v1/routing/VersionRoutingRequestHandlerSpec.scala b/test/v1/routing/VersionRoutingRequestHandlerSpec.scala index 6d61c15..c09f107 100644 --- a/test/v1/routing/VersionRoutingRequestHandlerSpec.scala +++ b/test/v1/routing/VersionRoutingRequestHandlerSpec.scala @@ -69,7 +69,7 @@ class VersionRoutingRequestHandlerSpec extends UnitSpec with Inside with MockApp val httpConfiguration: HttpConfiguration = HttpConfiguration("context") private val errorHandler = mock[HttpErrorHandler] private val filters = mock[HttpFilters] - (filters.filters _).stubs().returns(Seq.empty) + (() => filters.filters).stubs().returns(Seq.empty) MockAppConfig.featureSwitch.returns(Some(Configuration(ConfigFactory.parseString(""" |version-1.enabled = true diff --git a/test/v1/services/AmendDisclosuresServiceSpec.scala b/test/v1/services/AmendDisclosuresServiceSpec.scala index c0b9b36..6912106 100644 --- a/test/v1/services/AmendDisclosuresServiceSpec.scala +++ b/test/v1/services/AmendDisclosuresServiceSpec.scala @@ -73,7 +73,7 @@ class AmendDisclosuresServiceSpec extends ServiceSpec { s"a $ifsErrorCode error is returned from the service" in new Test { MockAmendDisclosuresConnector.amendDisclosures(amendDisclosuresRequest) - .returns(Future.successful(Left(ResponseWrapper(correlationId, DesErrors.single(DesErrorCode(ifsErrorCode)))))) + .returns(Future.successful(Left(ResponseWrapper(correlationId, DownstreamErrors.single(DownstreamErrorCode(ifsErrorCode)))))) await(service.amendDisclosures(amendDisclosuresRequest)) shouldBe Left(ErrorWrapper(correlationId, error)) } @@ -81,12 +81,12 @@ class AmendDisclosuresServiceSpec extends ServiceSpec { val input = Seq( ("INVALID_TAXABLE_ENTITY_ID", NinoFormatError), ("INVALID_TAX_YEAR", TaxYearFormatError), - ("INVALID_CORRELATIONID", DownstreamError), - ("INVALID_PAYLOAD", DownstreamError), + ("INVALID_CORRELATIONID", InternalError), + ("INVALID_PAYLOAD", InternalError), ("INCOME_SOURCE_NOT_FOUND", NotFoundError), ("VOLUNTARY_CLASS2_CANNOT_BE_CHANGED", RuleVoluntaryClass2CannotBeChangedError), - ("SERVER_ERROR", DownstreamError), - ("SERVICE_UNAVAILABLE", DownstreamError) + ("SERVER_ERROR", InternalError), + ("SERVICE_UNAVAILABLE", InternalError) ) input.foreach(args => (serviceError _).tupled(args)) diff --git a/test/v1/services/CreateMarriageAllowanceServiceSpec.scala b/test/v1/services/CreateMarriageAllowanceServiceSpec.scala index be8a74a..55f847b 100644 --- a/test/v1/services/CreateMarriageAllowanceServiceSpec.scala +++ b/test/v1/services/CreateMarriageAllowanceServiceSpec.scala @@ -60,7 +60,7 @@ class CreateMarriageAllowanceServiceSpec extends ServiceSpec { s"a $ifsErrorCode error is returned from the service" in new Test { MockCreateMarriageAllowanceConnector.create(createMarriageAllowanceRequest) - .returns(Future.successful(Left(ResponseWrapper(correlationId, DesErrors.single(DesErrorCode(ifsErrorCode)))))) + .returns(Future.successful(Left(ResponseWrapper(correlationId, DownstreamErrors.single(DownstreamErrorCode(ifsErrorCode)))))) await(service.create(createMarriageAllowanceRequest)) shouldBe Left(ErrorWrapper(correlationId, error)) } @@ -69,22 +69,22 @@ class CreateMarriageAllowanceServiceSpec extends ServiceSpec { ("INVALID_IDVALUE", NinoFormatError), ("DECEASED_PARTICIPANT", RuleDeceasedRecipientError), ("RELATIONSHIP_ALREADY_EXISTS", RuleActiveMarriageAllowanceClaimError), - ("INVALID_IDTYPE", DownstreamError), - ("END_DATE_CODE_NOT_FOUND", DownstreamError), - ("INVALID_CORRELATIONID", DownstreamError), - ("INVALID_PAYLOAD", DownstreamError), + ("INVALID_IDTYPE", InternalError), + ("END_DATE_CODE_NOT_FOUND", InternalError), + ("INVALID_CORRELATIONID", InternalError), + ("INVALID_PAYLOAD", InternalError), ("NINO_OR_TRN_NOT_FOUND", RuleInvalidRequestError), - ("INVALID_ACTUAL_END_DATE", DownstreamError), - ("INVALID_PARTICIPANT_END_DATE", DownstreamError), - ("INVALID_PARTICIPANT_START_DATE", DownstreamError), - ("INVALID_RELATIONSHIP_CODE", DownstreamError), - ("PARTICIPANT1_CANNOT_BE_UPDATED", DownstreamError), - ("PARTICIPANT2_CANNOT_BE_UPDATED", DownstreamError), - ("CONFIDENCE_CHECK_FAILED", DownstreamError), - ("CONFIDENCE_CHECK_SURNAME_MISSED", DownstreamError), - ("BAD_GATEWAY", DownstreamError), - ("SERVER_ERROR", DownstreamError), - ("SERVICE_UNAVAILABLE", DownstreamError) + ("INVALID_ACTUAL_END_DATE", InternalError), + ("INVALID_PARTICIPANT_END_DATE", InternalError), + ("INVALID_PARTICIPANT_START_DATE", InternalError), + ("INVALID_RELATIONSHIP_CODE", InternalError), + ("PARTICIPANT1_CANNOT_BE_UPDATED", InternalError), + ("PARTICIPANT2_CANNOT_BE_UPDATED", InternalError), + ("CONFIDENCE_CHECK_FAILED", InternalError), + ("CONFIDENCE_CHECK_SURNAME_MISSED", InternalError), + ("BAD_GATEWAY", InternalError), + ("SERVER_ERROR", InternalError), + ("SERVICE_UNAVAILABLE", InternalError) ) input.foreach(args => (serviceError _).tupled(args)) diff --git a/test/v1/services/DeleteRetrieveServiceSpec.scala b/test/v1/services/DeleteRetrieveServiceSpec.scala index 0941743..db71044 100644 --- a/test/v1/services/DeleteRetrieveServiceSpec.scala +++ b/test/v1/services/DeleteRetrieveServiceSpec.scala @@ -64,7 +64,7 @@ class DeleteRetrieveServiceSpec extends ServiceSpec { s"a $ifsErrorCode error is returned from the service" in new Test { MockDeleteRetrieveConnector.delete() - .returns(Future.successful(Left(ResponseWrapper(correlationId, DesErrors.single(DesErrorCode(ifsErrorCode)))))) + .returns(Future.successful(Left(ResponseWrapper(correlationId, DownstreamErrors.single(DownstreamErrorCode(ifsErrorCode)))))) await(service.delete()) shouldBe Left(ErrorWrapper(correlationId, error)) } @@ -72,10 +72,10 @@ class DeleteRetrieveServiceSpec extends ServiceSpec { val input = Seq( ("INVALID_TAXABLE_ENTITY_ID", NinoFormatError), ("INVALID_TAX_YEAR", TaxYearFormatError), - ("INVALID_CORRELATIONID", DownstreamError), + ("INVALID_CORRELATIONID", InternalError), ("NO_DATA_FOUND", NotFoundError), - ("SERVER_ERROR", DownstreamError), - ("SERVICE_UNAVAILABLE", DownstreamError) + ("SERVER_ERROR", InternalError), + ("SERVICE_UNAVAILABLE", InternalError) ) input.foreach(args => (serviceError _).tupled(args)) @@ -107,7 +107,7 @@ class DeleteRetrieveServiceSpec extends ServiceSpec { s"a $ifsErrorCode error is returned from the service" in new Test { MockDeleteRetrieveConnector.retrieve[Data]() - .returns(Future.successful(Left(ResponseWrapper(correlationId, DesErrors.single(DesErrorCode(ifsErrorCode)))))) + .returns(Future.successful(Left(ResponseWrapper(correlationId, DownstreamErrors.single(DownstreamErrorCode(ifsErrorCode)))))) await(service.retrieve[Data]()) shouldBe Left(ErrorWrapper(correlationId, error)) } @@ -115,10 +115,10 @@ class DeleteRetrieveServiceSpec extends ServiceSpec { val input = Seq( ("INVALID_TAXABLE_ENTITY_ID", NinoFormatError), ("INVALID_TAX_YEAR", TaxYearFormatError), - ("INVALID_CORRELATIONID", DownstreamError), + ("INVALID_CORRELATIONID", InternalError), ("NO_DATA_FOUND", NotFoundError), - ("SERVER_ERROR", DownstreamError), - ("SERVICE_UNAVAILABLE", DownstreamError) + ("SERVER_ERROR", InternalError), + ("SERVICE_UNAVAILABLE", InternalError) ) input.foreach(args => (serviceError _).tupled(args)) diff --git a/test/v1/services/EnrolmentsAuthServiceSpec.scala b/test/v1/services/EnrolmentsAuthServiceSpec.scala index e6410c2..08bbd01 100644 --- a/test/v1/services/EnrolmentsAuthServiceSpec.scala +++ b/test/v1/services/EnrolmentsAuthServiceSpec.scala @@ -25,7 +25,7 @@ import uk.gov.hmrc.auth.core.retrieve.v2.Retrievals._ import uk.gov.hmrc.auth.core.retrieve.{Retrieval, ~} import uk.gov.hmrc.http.HeaderCarrier import v1.models.auth.UserDetails -import v1.models.errors.{DownstreamError, UnauthorisedError} +import v1.models.errors.{InternalError, UnauthorisedError} import scala.concurrent.{ExecutionContext, Future} @@ -171,7 +171,7 @@ class EnrolmentsAuthServiceSpec extends ServiceSpec with MockAppConfig { MockAppConfig.confidenceLevelCheckEnabled.returns(ConfidenceLevelConfig(definitionEnabled = true, authValidationEnabled = false)) - val expected = Left(DownstreamError) + val expected = Left(InternalError) MockedAuthConnector.authorised(EmptyPredicate, authRetrievals) .returns(Future.successful(retrievalsResult)) diff --git a/test/v1/services/MtdIdLookupServiceSpec.scala b/test/v1/services/MtdIdLookupServiceSpec.scala index b067f7e..fe068d5 100644 --- a/test/v1/services/MtdIdLookupServiceSpec.scala +++ b/test/v1/services/MtdIdLookupServiceSpec.scala @@ -17,7 +17,7 @@ package v1.services import v1.mocks.connectors.MockMtdIdLookupConnector -import v1.models.errors.{DownstreamError, NinoFormatError, UnauthorisedError} +import v1.models.errors.{InternalError, NinoFormatError, UnauthorisedError} import scala.concurrent.Future @@ -62,7 +62,7 @@ class MtdIdLookupServiceSpec extends ServiceSpec { "a downstream error occurs the service" should { "proxy the error to the caller" in new Test { - val connectorResponse = Left(DownstreamError) + val connectorResponse = Left(InternalError) MockedMtdIdLookupConnector.lookup(nino) .returns(Future.successful(connectorResponse)) diff --git a/test/v1/support/DesResponseMappingSupportSpec.scala b/test/v1/support/DownstreamResponseMappingSupportSpec.scala similarity index 63% rename from test/v1/support/DesResponseMappingSupportSpec.scala rename to test/v1/support/DownstreamResponseMappingSupportSpec.scala index cc1eeb4..ce641c5 100644 --- a/test/v1/support/DesResponseMappingSupportSpec.scala +++ b/test/v1/support/DownstreamResponseMappingSupportSpec.scala @@ -23,12 +23,19 @@ import v1.controllers.EndpointLogContext import v1.models.errors._ import v1.models.outcomes.ResponseWrapper -class DesResponseMappingSupportSpec extends UnitSpec { +class DownstreamResponseMappingSupportSpec extends UnitSpec { implicit val logContext: EndpointLogContext = EndpointLogContext("ctrl", "ep") - val mapping: DesResponseMappingSupport with Logging = new DesResponseMappingSupport with Logging {} + val mapping: DownstreamResponseMappingSupport with Logging = new DownstreamResponseMappingSupport with Logging {} val correlationId: String = "someCorrelationId" + val errorCodeMap: PartialFunction[String, MtdError] = { + case "ERR1" => Error1 + case "ERR2" => Error2 + case "DS" => InternalError + } + + case class TestClass(field: Option[String]) object Error1 extends MtdError("msg", "code1") @@ -38,14 +45,6 @@ class DesResponseMappingSupportSpec extends UnitSpec { object ErrorBvr extends MtdError("msg", "bvr") - val errorCodeMap : PartialFunction[String, MtdError] = { - case "ERR1" => Error1 - case "ERR2" => Error2 - case "DS" => DownstreamError - } - - case class TestClass(field: Option[String]) - object TestClass { implicit val format: Format[TestClass] = Json.format[TestClass] } @@ -65,19 +64,19 @@ class DesResponseMappingSupportSpec extends UnitSpec { } } - "mapping Des errors" when { + "mapping downstream errors" when { "single error" when { "the error code is in the map provided" must { "use the mapping and wrap" in { - mapping.mapDesErrors(errorCodeMap)(ResponseWrapper(correlationId, DesErrors.single(DesErrorCode("ERR1")))) shouldBe + mapping.mapDownstreamErrors(errorCodeMap)(ResponseWrapper(correlationId, DownstreamErrors.single(DownstreamErrorCode("ERR1")))) shouldBe ErrorWrapper(correlationId, Error1) } } "the error code is not in the map provided" must { "default to DownstreamError and wrap" in { - mapping.mapDesErrors (errorCodeMap)(ResponseWrapper(correlationId, DesErrors.single(DesErrorCode("UNKNOWN")))) shouldBe - ErrorWrapper(correlationId, DownstreamError) + mapping.mapDownstreamErrors(errorCodeMap)(ResponseWrapper(correlationId, DownstreamErrors.single(DownstreamErrorCode("UNKNOWN")))) shouldBe + ErrorWrapper(correlationId, InternalError) } } } @@ -85,37 +84,37 @@ class DesResponseMappingSupportSpec extends UnitSpec { "multiple errors" when { "the error codes is in the map provided" must { "use the mapping and wrap with main error type of BadRequest" in { - mapping.mapDesErrors(errorCodeMap)(ResponseWrapper(correlationId, DesErrors(List(DesErrorCode("ERR1"), DesErrorCode("ERR2"))))) shouldBe - ErrorWrapper(correlationId, BadRequestError, Some(Seq(Error1, Error2))) + mapping.mapDownstreamErrors(errorCodeMap)(ResponseWrapper(correlationId, DownstreamErrors(List(DownstreamErrorCode("ERR1"), DownstreamErrorCode("ERR2"))))) shouldBe + ErrorWrapper(correlationId, BadRequestError, Some(List(Error1, Error2))) } } "the error code is not in the map provided" must { "default main error to DownstreamError ignore other errors" in { - mapping.mapDesErrors(errorCodeMap)(ResponseWrapper(correlationId, DesErrors(List(DesErrorCode("ERR1"), DesErrorCode("UNKNOWN"))))) shouldBe - ErrorWrapper(correlationId, DownstreamError) + mapping.mapDownstreamErrors(errorCodeMap)(ResponseWrapper(correlationId, DownstreamErrors(List(DownstreamErrorCode("ERR1"), DownstreamErrorCode("UNKNOWN"))))) shouldBe + ErrorWrapper(correlationId, InternalError) } } "one of the mapped errors is DownstreamError" must { "wrap the errors with main error type of DownstreamError" in { - mapping.mapDesErrors(errorCodeMap)(ResponseWrapper(correlationId, DesErrors(List(DesErrorCode("ERR1"), DesErrorCode("DS"))))) shouldBe - ErrorWrapper(correlationId, DownstreamError) + mapping.mapDownstreamErrors(errorCodeMap)(ResponseWrapper(correlationId, DownstreamErrors(List(DownstreamErrorCode("ERR1"), DownstreamErrorCode("DS"))))) shouldBe + ErrorWrapper(correlationId, InternalError) } } } "the error code is an OutboundError" must { "return the error as is (in an ErrorWrapper)" in { - mapping.mapDesErrors(errorCodeMap)(ResponseWrapper(correlationId, OutboundError(ErrorBvrMain))) shouldBe + mapping.mapDownstreamErrors(errorCodeMap)(ResponseWrapper(correlationId, OutboundError(ErrorBvrMain))) shouldBe ErrorWrapper(correlationId, ErrorBvrMain) } } "the error code is an OutboundError with multiple errors" must { "return the error as is (in an ErrorWrapper)" in { - mapping.mapDesErrors(errorCodeMap)(ResponseWrapper(correlationId, OutboundError(ErrorBvrMain, Some(Seq(ErrorBvr))))) shouldBe - ErrorWrapper(correlationId, ErrorBvrMain, Some(Seq(ErrorBvr))) + mapping.mapDownstreamErrors(errorCodeMap)(ResponseWrapper(correlationId, OutboundError(ErrorBvrMain, Some(List(ErrorBvr))))) shouldBe + ErrorWrapper(correlationId, ErrorBvrMain, Some(List(ErrorBvr))) } } }