Skip to content

Commit

Permalink
Merge pull request #6 from hmrc/MIBM-81
Browse files Browse the repository at this point in the history
[MIBM-81][JR] /goods-destination
  • Loading branch information
jordanrowe authored Sep 24, 2020
2 parents da2dfd5 + 7fcfb34 commit 16e5215
Show file tree
Hide file tree
Showing 25 changed files with 655 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2020 HM Revenue & Customs
*
*/

package uk.gov.hmrc.merchandiseinbaggagefrontend.controllers

import javax.inject.{Inject, Singleton}
import play.api.data.Form
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
import uk.gov.hmrc.merchandiseinbaggagefrontend.config.AppConfig
import uk.gov.hmrc.merchandiseinbaggagefrontend.forms.GoodsDestinationFormProvider
import uk.gov.hmrc.merchandiseinbaggagefrontend.model.core.GoodsDestination
import uk.gov.hmrc.merchandiseinbaggagefrontend.views.html.GoodsDestinationView
import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendBaseController

@Singleton
class GoodsDestinationController @Inject()(
override val controllerComponents: MessagesControllerComponents,
formProvider: GoodsDestinationFormProvider,
view: GoodsDestinationView
)(implicit val appConfig: AppConfig) extends FrontendBaseController {

val form: Form[GoodsDestination] = formProvider()

def onPageLoad(): Action[AnyContent] = Action { implicit request =>
Ok(view(form))
}

//TODO implement once session storage has been done under MIBM-77
def onSubmit(): Action[AnyContent] = Action { implicit request =>
form
.bindFromRequest()
.fold(
formWithErrors => BadRequest(view(formWithErrors)),
value => Ok
)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright 2020 HM Revenue & Customs
*
*/

package uk.gov.hmrc.merchandiseinbaggagefrontend.forms

import javax.inject.Inject
import play.api.data.Form
import uk.gov.hmrc.merchandiseinbaggagefrontend.forms.mappings.Mappings
import uk.gov.hmrc.merchandiseinbaggagefrontend.model.core.GoodsDestination

class GoodsDestinationFormProvider @Inject() extends Mappings {

def apply(): Form[GoodsDestination] =
Form(
"value" -> enumerable[GoodsDestination]("goodsDestination.error.required")
)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2020 HM Revenue & Customs
*
*/

package uk.gov.hmrc.merchandiseinbaggagefrontend.forms.mappings

import play.api.data.FormError
import play.api.data.format.Formatter
import uk.gov.hmrc.merchandiseinbaggagefrontend.model.Enumerable


trait Formatters {

private[mappings] def stringFormatter(errorKey: String): Formatter[String] = new Formatter[String] {

override def bind(key: String, data: Map[String, String]): Either[Seq[FormError], String] =
data.get(key) match {
case None | Some("") => Left(Seq(FormError(key, errorKey)))
case Some(s) => Right(s)
}

override def unbind(key: String, value: String): Map[String, String] =
Map(key -> value)
}

private[mappings] def enumerableFormatter[A](requiredKey: String, invalidKey: String)(implicit ev: Enumerable[A]): Formatter[A] =
new Formatter[A] {

private val baseFormatter = stringFormatter(requiredKey)

override def bind(key: String, data: Map[String, String]): Either[Seq[FormError], A] =
baseFormatter.bind(key, data).right.flatMap { str =>
ev.withName(str).map(Right.apply).getOrElse(Left(Seq(FormError(key, invalidKey))))
}

override def unbind(key: String, value: A): Map[String, String] =
baseFormatter.unbind(key, value.toString)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright 2020 HM Revenue & Customs
*
*/

package uk.gov.hmrc.merchandiseinbaggagefrontend.forms.mappings

import play.api.data.FieldMapping
import play.api.data.Forms.of
import uk.gov.hmrc.merchandiseinbaggagefrontend.model.Enumerable

trait Mappings extends Formatters {

protected def enumerable[A](requiredKey: String = "error.required", invalidKey: String = "error.invalid")(
implicit ev: Enumerable[A]): FieldMapping[A] =
of(enumerableFormatter[A](requiredKey, invalidKey))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2020 HM Revenue & Customs
*
*/

package uk.gov.hmrc.merchandiseinbaggagefrontend.model

import play.api.libs.json._

trait Enumerable[A] {

def withName(str: String): Option[A]
}

object Enumerable {

def apply[A](entries: (String, A)*): Enumerable[A] =
new Enumerable[A] {
override def withName(str: String): Option[A] =
entries.toMap.get(str)
}

trait Implicits {

implicit def reads[A](implicit ev: Enumerable[A]): Reads[A] =
Reads {
case JsString(str) =>
ev.withName(str)
.map { s =>
JsSuccess(s)
}
.getOrElse(JsError("error.invalid"))
case _ =>
JsError("error.invalid")
}

implicit def writes[A: Enumerable]: Writes[A] =
Writes(value => JsString(value.toString))
}
}

class WithName(string: String) {
override val toString: String = string
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2020 HM Revenue & Customs
*
*/

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

import play.api.data.Form
import play.api.i18n.Messages
import uk.gov.hmrc.govukfrontend.views.Aliases.Text
import uk.gov.hmrc.govukfrontend.views.viewmodels.radios.RadioItem
import uk.gov.hmrc.merchandiseinbaggagefrontend.model.{Enumerable, WithName}

sealed trait GoodsDestination

object GoodsDestination extends Enumerable.Implicits {

case object NorthernIreland extends WithName("ni") with GoodsDestination
case object EngScoWal extends WithName("gb") with GoodsDestination

val values: Seq[GoodsDestination] = Seq(
NorthernIreland,
EngScoWal
)

def options(form: Form[_])(implicit messages: Messages): Seq[RadioItem] = values.map { value =>
RadioItem(
value = Some(value.toString),
content = Text(messages(s"goodsDestination.${value.toString}")),
checked = form("value").value.contains(value.toString)
)
}


implicit val enumerable: Enumerable[GoodsDestination] =
Enumerable(values.map(v => v.toString -> v): _*)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ import uk.gov.hmrc.http.{HeaderCarrier, HttpClient, HttpResponse}
import uk.gov.hmrc.merchandiseinbaggagefrontend.config.PaymentServiceConf
import uk.gov.hmrc.merchandiseinbaggagefrontend.model.api.{PayApiResponse, PayApitRequest}
import uk.gov.hmrc.merchandiseinbaggagefrontend.utils.SessionIdGenerator
import uk.gov.hmrc.http.HttpReads.Implicits._

import scala.concurrent.{ExecutionContext, Future}

trait PaymentService extends PaymentServiceConf with SessionIdGenerator {

def makePayment(httpClient: HttpClient, requestBody: PayApitRequest)(implicit hc: HeaderCarrier, ec: ExecutionContext): Future[HttpResponse] = {
httpClient.POST(s"$paymentBaseUri${paymentServiceConf.url.value}", Json.toJson(requestBody), addSessionId(hc).headers)
httpClient.POST[PayApitRequest, HttpResponse](s"$paymentBaseUri${paymentServiceConf.url.value}", requestBody, addSessionId(hc).headers)
}

protected def extractUrl(response: HttpResponse): PayApiResponse =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@*
* Copyright 2020 HM Revenue & Customs
*
*@

@import uk.gov.hmrc.govukfrontend.views.html.components
@import uk.gov.hmrc.merchandiseinbaggagefrontend.model.core.GoodsDestination
@import uk.gov.hmrc.merchandiseinbaggagefrontend.controllers.routes._

@this(
layout: Layout,
formHelper: FormWithCSRF,
errorSummary: components.errorSummary,
inputRadio: components.inputRadio,
button: components.button
)

@(form: Form[_])(implicit request: Request[_], messages: Messages, appConfig: AppConfig)

@layout(pageTitle = Some(messages("goodsDestination.title"))) {
@formHelper(action = GoodsDestinationController.onSubmit(), 'autoComplete -> "off", 'novalidate -> "novalidate") {

@errorSummary(form.errors)

@inputRadio(
form = form,
legend = messages("goodsDestination.heading"),
legendAsHeading = true,
items = GoodsDestination.options(form)
)

@button("site.continue")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@*
* Copyright 2020 HM Revenue & Customs
*
*@

@this(govukErrorSummary: GovukErrorSummary)

@(errors: Seq[FormError], errorFieldName: Option[String] = None)(implicit messages: Messages)

@if(errors.nonEmpty) {
@defining(errors.map { error =>
ErrorLink(
href = Some(s"#${errorFieldName.getOrElse(error.key)}"),
content = Text(messages(error.message, error.args:_*))
)
}) { errorLinks =>
@govukErrorSummary(ErrorSummary(
errorList = errorLinks,
title = Text(messages("error.summary.title"))
))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@*
* Copyright 2020 HM Revenue & Customs
*
*@

@this(govukRadios: GovukRadios)

@( form: Form[_],
legend: String,
items: Seq[RadioItem],
legendAsHeading: Boolean = true,
hintKey: Option[String] = None,
classes: String = "govuk-fieldset__legend--xl",
inline: Boolean = false
)(implicit messages: Messages)

@govukRadios(Radios(
classes = if(inline) "govuk-radios--inline" else "",
name = "value",
fieldset = Some(Fieldset(
legend = Some(Legend(
content = Text(messages(legend)),
isPageHeading = legendAsHeading,
classes = classes
))
)),
hint = hintKey.map { hint => Hint(
content = Text(messages(hint))
)},
items = items,
errorMessage = form("value").error.map(err => ErrorMessage(content = Text(messages(err.message, err.args:_*))))
))
7 changes: 6 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ lazy val microservice = Project(appName, file("."))
// ***************
// Use the silencer plugin to suppress warnings
// You may turn it on for `views` too to suppress warnings from unused imports in compiled twirl templates, but this will hide other warnings.
scalacOptions += "-P:silencer:pathFilters=routes",
// silence all warnings on autogenerated files
scalacOptions += "-P:silencer:pathFilters=target/.*",
// Make sure you only exclude warnings for the project directories, i.e. make builds reproducible
scalacOptions += s"-P:silencer:sourceRoots=${baseDirectory.value.getCanonicalPath}",
// Suppress warnings due to mongo dates using `$date` in their Json representation
scalacOptions += "-P:silencer:globalFilters=possible missing interpolator: detected interpolated identifier `\\$date`",
libraryDependencies ++= Seq(
compilerPlugin("com.github.ghik" % "silencer-plugin" % silencerVersion cross CrossVersion.full),
"com.github.ghik" % "silencer-lib" % silencerVersion % Provided cross CrossVersion.full
Expand Down
4 changes: 4 additions & 0 deletions conf/app.routes
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ GET /trader-address-list uk.gov.hmrc.merchandiseinbaggagefronten
GET /trader-eori uk.gov.hmrc.merchandiseinbaggagefrontend.controllers.SkeletonJourneyController.traderEori
GET /trader-journey uk.gov.hmrc.merchandiseinbaggagefrontend.controllers.SkeletonJourneyController.traderJourney

# GoodsDestination
GET /goods-destination uk.gov.hmrc.merchandiseinbaggagefrontend.controllers.GoodsDestinationController.onPageLoad()
POST /goods-destination uk.gov.hmrc.merchandiseinbaggagefrontend.controllers.GoodsDestinationController.onSubmit()

GET /check-your-answers uk.gov.hmrc.merchandiseinbaggagefrontend.controllers.CheckYourAnswersController.onPageLoad
12 changes: 12 additions & 0 deletions conf/messages
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ footer.termsConditions.url = /help/terms-and-conditions
footer.govukHelp.text = Help using GOV.UK
footer.govukHelp.url = https://www.gov.uk/help

error.summary.title = There is a problem

site.continue = Continue

payment.button = Pay now
next.button = Next

Expand All @@ -32,3 +36,11 @@ traderAddressList.title = Select your address
traderEori.title = What is your EORI number?
traderJourney.title = What your journey details?
checkYourAnswers.title = Check your answers before making your declaration


# GoodsDestination
goodsDestination.title = Where in the UK are the goods going?
goodsDestination.heading = Where in the UK are the goods going?
goodsDestination.ni = Northern Ireland
goodsDestination.gb = England, Wales or Scotland
goodsDestination.error.required = Select one of the options below
5 changes: 3 additions & 2 deletions project/AppDependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ object AppDependencies {
"org.jsoup" % "jsoup" % "1.10.2" % Test,
"com.typesafe.play" %% "play-test" % current % Test,
"com.vladsch.flexmark" % "flexmark-all" % "0.35.10" % "test, it",
"org.scalatestplus.play" %% "scalatestplus-play" % "4.0.3" % "test, it",
"com.github.tomakehurst" % "wiremock-standalone" % "2.27.1" % Test
"org.scalatestplus.play" %% "scalatestplus-play" % "3.1.2" % "test, it",
"org.scalatestplus" %% "scalacheck-1-14" % "3.2.2.0" % Test,
"com.github.tomakehurst" % "wiremock-standalone" % "2.27.1" % Test
)
}
9 changes: 8 additions & 1 deletion test/uk/gov/hmrc/merchandiseinbaggagefrontend/BaseSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,24 @@ import org.scalatest.BeforeAndAfterEach
import org.scalatest.matchers.must.Matchers
import org.scalatest.wordspec.AnyWordSpec
import org.scalatestplus.play.guice.GuiceOneAppPerSuite
import play.api.i18n.MessagesApi
import play.api.inject.Injector
import play.api.mvc.AnyContentAsEmpty
import play.api.mvc.{AnyContentAsEmpty, MessagesControllerComponents}
import play.api.test.CSRFTokenHelper._
import play.api.test.FakeRequest
import play.api.test.Helpers.GET
import uk.gov.hmrc.merchandiseinbaggagefrontend.config.AppConfig

trait BaseSpec extends AnyWordSpec with Matchers with BeforeAndAfterEach

trait BaseSpecWithApplication extends BaseSpec with GuiceOneAppPerSuite {
lazy val injector: Injector = app.injector

implicit val appConfig = injector.instanceOf[AppConfig]

val messagesApi = injector.instanceOf[MessagesApi]
val controllerComponents = injector.instanceOf[MessagesControllerComponents]

def buildGet(url: String): FakeRequest[AnyContentAsEmpty.type] =
FakeRequest(GET, url).withCSRFToken.asInstanceOf[FakeRequest[AnyContentAsEmpty.type]]
}
Expand Down
Loading

0 comments on commit 16e5215

Please sign in to comment.