Skip to content

Commit

Permalink
Mibm 153 (#114)
Browse files Browse the repository at this point in the history
* MIB-153: backend connector

* MIB-142_V3: unused import

* MIB-142_V3: only reset when persistence succeed

* MIB-153: remove chance of throwing in mib generator

* MIB-153: little cleaning up

* MIB-153: add declarationId retrieved from backend

* MIB-153: cleaning up

* MIB-153: renaming

* MIB-153: unused import

* MIB-153: rename

* MIB-153: inline 2 vals

* MIB-153: inline one more val

* [MIBM-153][PH] fix deprecation warning

* [MIBM-153][PH] rename makePayment method

Co-authored-by: Paul Hodgson <[email protected]>
  • Loading branch information
PasGat and PaulHodgson authored Nov 19, 2020
1 parent f067c39 commit c74c4ac
Show file tree
Hide file tree
Showing 18 changed files with 311 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import pureconfig.generic.auto._ // Do not remove this
import uk.gov.hmrc.merchandiseinbaggagefrontend.config.AppConfigSource.configSource

@Singleton
class AppConfig() extends MongoConfiguration {
class AppConfig() extends MongoConfiguration with MibConfiguration {
lazy val footerLinkItems: Seq[String] = configSource("footerLinkItems").loadOrThrow[Seq[String]]

private val serviceIdentifier = "mib"
Expand All @@ -33,12 +33,21 @@ class AppConfig() extends MongoConfiguration {
val contactUrl = s"$contactHost/contact/contact-hmrc-unauthenticated?service=$serviceIdentifier"
}

object AppConfigSource {
val configSource: String => ConfigSource = ConfigSource.default.at
}

trait MongoConfiguration {
lazy val mongoConf: MongoConf = configSource("mongodb").loadOrThrow[MongoConf]
}

final case class MongoConf(uri: String, host: String = "localhost", port: Int = 27017, collectionName: String = "declaration")

object AppConfigSource {
val configSource: String => ConfigSource = ConfigSource.default.at
}
trait MibConfiguration {
lazy val mibConf: MIBConf = configSource("microservice.services.merchandise-in-baggage").loadOrThrow[MIBConf]
import mibConf._
lazy val declarationsUrl: String = "/merchandise-in-baggage/declarations"
lazy val persistDeclarationsUrl: String = s"$protocol://$host:$port$declarationsUrl"
}

final case class MIBConf(protocol: String, host: String, port: Int)
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class MerchandiseInBaggageFrontendConfigModule(unused: Environment, configuratio
override def configure(): Unit = {
bindBaseUrl("currencyConversionBaseUrl", "currency-conversion")
bindBaseUrl("paymentBaseUrl", "payment")
bindBaseUrl("mibBackendBaseUrl", "merchandise-in-baggage")
bindBaseUrl("addressLookupFrontendBaseUrl", "address-lookup-frontend")
bindConstant().annotatedWith(named("addressLookupCallback"))
.to(servicesConfig.getString("microservice.services.address-lookup-frontend.callback"))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2020 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package uk.gov.hmrc.merchandiseinbaggagefrontend.connectors

import javax.inject.{Inject, Named, Singleton}
import uk.gov.hmrc.http.{HeaderCarrier, HttpClient}
import uk.gov.hmrc.merchandiseinbaggagefrontend.config.MibConfiguration
import uk.gov.hmrc.merchandiseinbaggagefrontend.model.core.{Declaration, DeclarationId}
import uk.gov.hmrc.http.HttpReads.Implicits.readFromJson
import scala.concurrent.{ExecutionContext, Future}

@Singleton
class MibConnector @Inject()(httpClient: HttpClient, @Named("mibBackendBaseUrl") base: String) extends MibConfiguration {

def persistDeclaration(declaration: Declaration)(implicit hc: HeaderCarrier, ec: ExecutionContext): Future[DeclarationId] =
httpClient.POST[Declaration, DeclarationId](s"$base$declarationsUrl", declaration)

def findDeclaration(declarationId: DeclarationId)(implicit hc: HeaderCarrier, ec: ExecutionContext): Future[Declaration] =
httpClient.GET[Declaration](s"$base$declarationsUrl/${declarationId.value}")
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import scala.concurrent.{ExecutionContext, Future}

@Singleton
class PaymentConnector @Inject()(httpClient: HttpClient, @Named("paymentBaseUrl") baseUrl: String) {
def makePayment(requestBody: PayApiRequest)(implicit hc: HeaderCarrier, ec: ExecutionContext): Future[HttpResponse] = {
def createPaymentSession(requestBody: PayApiRequest)(implicit hc: HeaderCarrier, ec: ExecutionContext): Future[HttpResponse] = {
httpClient.POST[PayApiRequest, HttpResponse](
s"$baseUrl$payUrl", requestBody)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ package uk.gov.hmrc.merchandiseinbaggagefrontend.controllers

import javax.inject.{Inject, Singleton}
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents, Result}
import reactivemongo.api.commands.UpdateWriteResult
import uk.gov.hmrc.http.{HeaderCarrier, HttpResponse}
import uk.gov.hmrc.merchandiseinbaggagefrontend.config.AppConfig
import uk.gov.hmrc.merchandiseinbaggagefrontend.connectors.PaymentConnector
import uk.gov.hmrc.merchandiseinbaggagefrontend.connectors.{MibConnector, PaymentConnector}
import uk.gov.hmrc.merchandiseinbaggagefrontend.forms.CheckYourAnswersForm.form
import uk.gov.hmrc.merchandiseinbaggagefrontend.model.api.PayApiRequestBuilder
import uk.gov.hmrc.merchandiseinbaggagefrontend.model.core.DeclarationType.{Export, Import}
import uk.gov.hmrc.merchandiseinbaggagefrontend.model.core.{DeclarationGoods, GoodsEntries, GoodsEntry}
import uk.gov.hmrc.merchandiseinbaggagefrontend.model.core.{Declaration, DeclarationGoods, DeclarationId, DeclarationJourney, GoodsEntries, GoodsEntry}
import uk.gov.hmrc.merchandiseinbaggagefrontend.repositories.DeclarationJourneyRepository
import uk.gov.hmrc.merchandiseinbaggagefrontend.service.CalculationService
import uk.gov.hmrc.merchandiseinbaggagefrontend.views.html.CheckYourAnswersPage
Expand All @@ -36,6 +37,7 @@ class CheckYourAnswersController @Inject()(override val controllerComponents: Me
actionProvider: DeclarationJourneyActionProvider,
calculationService: CalculationService,
connector: PaymentConnector,
mibConnector: MibConnector,
override val repo: DeclarationJourneyRepository,
page: CheckYourAnswersPage)
(implicit ec: ExecutionContext, appConfig: AppConfig)
Expand All @@ -56,8 +58,8 @@ class CheckYourAnswersController @Inject()(override val controllerComponents: Me
}

val onSubmit: Action[AnyContent] = actionProvider.journeyAction.async { implicit request =>
request.declarationJourney.goodsEntries.declarationGoodsIfComplete
.fold(actionProvider.invalidRequestF)(goods => declarationConfirmation(request, goods))
request.declarationJourney.declarationIfRequiredAndComplete
.fold(actionProvider.invalidRequestF)(declaration => declarationConfirmation(declaration))
}

val addMoreGoods: Action[AnyContent] = actionProvider.journeyAction.async { implicit request =>
Expand All @@ -72,17 +74,34 @@ class CheckYourAnswersController @Inject()(override val controllerComponents: Me
}
}

private def declarationConfirmation(request: DeclarationJourneyRequest[AnyContent], goods: DeclarationGoods)
(implicit headerCarrier: HeaderCarrier): Future[Result] = {
private def declarationConfirmation(declaration: Declaration)
(implicit request: DeclarationJourneyRequest[AnyContent]): Future[Result] =
request.declarationJourney.declarationType match {
case Export => Future.successful(Redirect(routes.DeclarationConfirmationController.onPageLoad()))
case Import => makePayment(goods).map(res => Redirect(connector.extractUrl(res).nextUrl.value))
case Export => mibConnector.persistDeclaration(declaration).flatMap(resetAndRedirect)
case Import => processImportDeclaration(declaration)
}
}

private def makePayment(goods: DeclarationGoods)(implicit headerCarrier: HeaderCarrier): Future[HttpResponse] =
private def processImportDeclaration(declaration: Declaration)
(implicit request: DeclarationJourneyRequest[AnyContent]): Future[Result] =
for {
persist <- mibConnector.persistDeclaration(declaration)
pay <- createPaymentSession(declaration.declarationGoods)
_ <- resetJourney(persist)
} yield Redirect(connector.extractUrl(pay).nextUrl.value)

private def createPaymentSession(goods: DeclarationGoods)(implicit headerCarrier: HeaderCarrier): Future[HttpResponse] =
for {
payApiRequest <- buildRequest(goods, calculationService.paymentCalculation)
response <- connector.makePayment(payApiRequest)
response <- connector.createPaymentSession(payApiRequest)
} yield response

private def resetAndRedirect(declarationId: DeclarationId)
(implicit request: DeclarationJourneyRequest[AnyContent]): Future[Result] =
resetJourney(declarationId).map(_ => Redirect(routes.DeclarationConfirmationController.onPageLoad()))


private def resetJourney(id: DeclarationId)(implicit request: DeclarationJourneyRequest[AnyContent]): Future[UpdateWriteResult] = {
import request.declarationJourney._
repo.upsert(DeclarationJourney(sessionId, declarationType, declarationId = Some(id)))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,23 @@ package uk.gov.hmrc.merchandiseinbaggagefrontend.controllers

import javax.inject.Inject
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
import reactivemongo.api.commands.UpdateWriteResult
import uk.gov.hmrc.merchandiseinbaggagefrontend.config.AppConfig
import uk.gov.hmrc.merchandiseinbaggagefrontend.model.core.DeclarationJourney
import uk.gov.hmrc.merchandiseinbaggagefrontend.repositories.DeclarationJourneyRepository
import uk.gov.hmrc.merchandiseinbaggagefrontend.connectors.MibConnector
import uk.gov.hmrc.merchandiseinbaggagefrontend.views.html.DeclarationConfirmationView

import scala.concurrent.{ExecutionContext, Future}
import scala.concurrent.ExecutionContext

class DeclarationConfirmationController @Inject()(
override val controllerComponents: MessagesControllerComponents,
actionProvider: DeclarationJourneyActionProvider,
view: DeclarationConfirmationView,
repo: DeclarationJourneyRepository
connector: MibConnector
)(implicit ec: ExecutionContext, appConf: AppConfig)
extends DeclarationJourneyController {

val onPageLoad: Action[AnyContent] = actionProvider.journeyAction.async { implicit request =>
request.declarationJourney.declarationIfRequiredAndComplete.fold(actionProvider.invalidRequestF) { declaration =>
resetJourney.map(_ => Ok(view(declaration)))
request.declarationJourney.declarationId.fold(actionProvider.invalidRequestF) { declarationId =>
connector.findDeclaration(declarationId).map(declaration => Ok(view(declaration)))
}
}

private def resetJourney(implicit request: DeclarationJourneyRequest[AnyContent]): Future[UpdateWriteResult] = {
import request.declarationJourney._
repo.upsert(DeclarationJourney(sessionId, declarationType))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,9 @@ case class DeclarationJourney(sessionId: SessionId,
maybeJourneyDetailsEntry: Option[JourneyDetailsEntry] = None,
maybeTravellingByVehicle: Option[YesNo] = None,
maybeTravellingBySmallVehicle: Option[YesNo] = None,
maybeRegistrationNumber: Option[String] = None) {
maybeRegistrationNumber: Option[String] = None,
declarationId: Option[DeclarationId] = None
) {

val maybeCustomsAgent: Option[CustomsAgent] =
for {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2020 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package uk.gov.hmrc.merchandiseinbaggagefrontend.model.core

import play.api.libs.json._
import uk.gov.hmrc.merchandiseinbaggagefrontend.utils.ValueClassFormat

case class DeclarationId(value: String)
object DeclarationId {
implicit val format: Format[DeclarationId] = ValueClassFormat.format(value => DeclarationId.apply(value))(_.value)
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,19 @@ trait MibReferenceGenerator {

protected val referenceFormat = """^X([A-Z])MB(\d{10})$"""
protected val referenceFormatRegex = referenceFormat.r
private def uniqueId: String = java.util.UUID.randomUUID().toString

def mibReference: MibReference = {
//TODO this could throw
val chars: Char = Random.alphanumeric.find(c => c.isLetter && c.isUpper).getOrElse('A')
MibReference(s"X${chars}MB${java.nio.ByteBuffer.wrap(uniqueId.getBytes).asLongBuffer().get().toString.take(10)}")
val digits: List[Int] = (0 to 9).toList

val randomDigits: List[Int] = digits.flatMap { _ =>
val idx = randomIndex(digits.size)
digits.find(_ == idx)
}

val randomCapital = Random.alphanumeric.filter(c => c.isLetter && c.isUpper).take(1).headOption.getOrElse('A')

MibReference(s"X${randomCapital}MB${randomDigits.mkString}")
}

private def randomIndex(size: Int): Int = Random.nextInt(size)
}
3 changes: 2 additions & 1 deletion test/uk/gov/hmrc/merchandiseinbaggagefrontend/BaseSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ trait BaseSpecWithApplication extends BaseSpec
"play.http.router" -> "testOnlyDoNotUseInAppConf.Routes",
"microservice.services.address-lookup-frontend.port" -> WireMockSupport.port,
"microservice.services.currency-conversion.port" -> WireMockSupport.port,
"microservice.services.payment.port" -> WireMockSupport.port
"microservice.services.payment.port" -> WireMockSupport.port,
"microservice.services.merchandise-in-baggage.port" -> WireMockSupport.port
)).build()

lazy val declarationJourneyRepository: DeclarationJourneyRepository = injector.instanceOf[DeclarationJourneyRepository]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2020 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package uk.gov.hmrc.merchandiseinbaggagefrontend.connectors

import uk.gov.hmrc.http.HeaderCarrier
import uk.gov.hmrc.merchandiseinbaggagefrontend.model.core.DeclarationId
import uk.gov.hmrc.merchandiseinbaggagefrontend.stubs.MibBackendStub._
import uk.gov.hmrc.merchandiseinbaggagefrontend.{BaseSpecWithApplication, CoreTestData, WireMockSupport}

import scala.concurrent.ExecutionContext.Implicits.global

class MibConnectorSpec extends BaseSpecWithApplication with CoreTestData with WireMockSupport {

val client = app.injector.instanceOf[MibConnector]
implicit val hc = HeaderCarrier()

"send a declaration to backend to be persisted" in {
givenDeclarationIsPersistedInBackend(wireMockServer, declaration)

client.persistDeclaration(declaration).futureValue mustBe stubbedDeclarationId
}

"find a persisted declaration from backend by declarationId" in {
val id = DeclarationId("1234")

givenPersistedDeclarationIsFound(wireMockServer, declaration, id)

client.findDeclaration(id).futureValue mustBe declaration
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class PaymentConnectorSpec extends BaseSpecWithApplication with WireMockSupport
.willReturn(okJson(stubbedResponse).withStatus(201))
)

val response: HttpResponse = makePayment(payApiRequest).futureValue
val response: HttpResponse = createPaymentSession(payApiRequest).futureValue
response.status mustBe 201
response.body mustBe stubbedResponse
}
Expand Down
Loading

0 comments on commit c74c4ac

Please sign in to comment.