Skip to content

Commit

Permalink
HIPP-1583: Add Produce API short description page (#406)
Browse files Browse the repository at this point in the history
* HIPP-1583: Add Produce API short description page

* HIPP-1583: Address comments
  • Loading branch information
victorarbuesmallada authored Oct 17, 2024
1 parent af2f75d commit af55e90
Show file tree
Hide file tree
Showing 16 changed files with 485 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,57 @@

package controllers.myapis.produce

import controllers.actions.*
import controllers.actions._
import forms.myapis.produce.ProduceApiShortDescriptionFormProvider
import models.Mode
import play.api.data.Form
import navigation.Navigator
import pages.myapis.produce.ProduceApiShortDescriptionPage
import play.api.i18n.{I18nSupport, MessagesApi}
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
import repositories.{ProduceApiSessionRepository, SessionRepository}
import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendBaseController
import views.html.myapis.produce.ProduceApiShortDescriptionView

import javax.inject.Inject
import scala.concurrent.{ExecutionContext, Future}

class ProduceApiShortDescriptionController @Inject()(
override val messagesApi: MessagesApi,
identify: IdentifierAction,
val controllerComponents: MessagesControllerComponents,
view: ProduceApiShortDescriptionView
)(implicit ec: ExecutionContext) extends FrontendBaseController with I18nSupport {

def onPageLoad(mode: Mode): Action[AnyContent] = identify {
implicit request => Ok(view(mode))
override val messagesApi: MessagesApi,
sessionRepository: ProduceApiSessionRepository,
navigator: Navigator,
identify: IdentifierAction,
getData: ProduceApiDataRetrievalAction,
requireData: DataRequiredAction,
formProvider: ProduceApiShortDescriptionFormProvider,
val controllerComponents: MessagesControllerComponents,
view: ProduceApiShortDescriptionView
)(implicit ec: ExecutionContext) extends FrontendBaseController with I18nSupport {

val form = formProvider()

def onPageLoad(mode: Mode): Action[AnyContent] = (identify andThen getData andThen requireData) {
implicit request =>

val preparedForm = request.userAnswers.get(ProduceApiShortDescriptionPage) match {
case None => form
case Some(value) => form.fill(value)
}

Ok(view(preparedForm, mode, request.user))
}

def onSubmit(mode: Mode): Action[AnyContent] = identify {
implicit request => Redirect(routes.ProduceApiReviewAppearanceController.onPageLoad())
def onSubmit(mode: Mode): Action[AnyContent] = (identify andThen getData andThen requireData).async {
implicit request =>

form.bindFromRequest().fold(
formWithErrors =>
Future.successful(BadRequest(view(formWithErrors, mode, request.user))),

value =>
for {
updatedAnswers <- Future.fromTry(request.userAnswers.set(ProduceApiShortDescriptionPage, value))
_ <- sessionRepository.set(updatedAnswers)
} yield Redirect(navigator.nextPage(ProduceApiShortDescriptionPage, mode, updatedAnswers))
)
}
}
8 changes: 8 additions & 0 deletions app/forms/mappings/Constraints.scala
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ trait Constraints {
Invalid(errorKey, maximum)
}

protected def minLength(minimum: Int, errorKey: String): Constraint[String] =
Constraint {
case str if str.length >= minimum =>
Valid
case _ =>
Invalid(errorKey, minimum)
}

protected def maxDate(maximum: LocalDate, errorKey: String, args: Any*): Constraint[LocalDate] =
Constraint {
case date if date.isAfter(maximum) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2024 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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 forms.myapis.produce

import forms.mappings.Mappings
import play.api.data.Form

import javax.inject.Inject

class ProduceApiShortDescriptionFormProvider @Inject() extends Mappings {

def apply(): Form[String] =
Form(
"value" -> text("produceApiShortDescription.error.required")
.transform[String](_.trim, identity)
.verifying(maxLength(180, "produceApiShortDescription.error.length"))
.verifying(minLength(8, "produceApiShortDescription.error.length"))
)
}
27 changes: 27 additions & 0 deletions app/pages/myapis/produce/ProduceApiShortDescriptionPage.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2024 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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 pages.myapis.produce

import pages.QuestionPage
import play.api.libs.json.JsPath

case object ProduceApiShortDescriptionPage extends QuestionPage[String] {

override def path: JsPath = JsPath \ toString

override def toString: String = "produceApiShortDescription"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2024 HM Revenue & Customs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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 viewmodels.checkAnswers.myapis.produce

import controllers.myapis.produce.routes
import models.{CheckMode, UserAnswers}
import pages.myapis.produce.ProduceApiShortDescriptionPage
import play.api.i18n.Messages
import play.twirl.api.HtmlFormat
import uk.gov.hmrc.govukfrontend.views.viewmodels.summarylist.SummaryListRow
import viewmodels.govuk.summarylist._
import viewmodels.implicits._

object ProduceApiShortDescriptionSummary {

def row(answers: UserAnswers)(implicit messages: Messages): Option[SummaryListRow] =
answers.get(ProduceApiShortDescriptionPage).map {
answer =>

SummaryListRowViewModel(
key = "produceApiShortDescription.checkYourAnswersLabel",
value = ValueViewModel(HtmlFormat.escape(answer).toString),
actions = Seq(
ActionItemViewModel("site.change", routes.ProduceApiShortDescriptionController.onPageLoad(CheckMode).url)
.withVisuallyHiddenText(messages("produceApiShortDescription.change.hidden"))
)
)
}
}
40 changes: 36 additions & 4 deletions app/views/myapis/produce/ProduceApiShortDescriptionView.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,51 @@
* limitations under the License.
*@

@import models.myapis.produce.ProduceApiHowToCreate
@import models.user.UserModel
@import viewmodels.InputWidth._
@import uk.gov.hmrc.govukfrontend.views.html.components.CharacterCount

@this(
layout: templates.Layout,
formHelper: FormWithCSRF,
govukErrorSummary: GovukErrorSummary,
govukCharacterCount: GovukCharacterCount,
govukButton: GovukButton
)

@(mode: Mode)(implicit request: Request[?], messages: Messages)
@(form: Form[?], mode: Mode, userModel: UserModel)(implicit request: Request[?], messages: Messages)

@hintContent() = {
<h1 class="govuk-label-wrapper">
<label class="govuk-label govuk-label--l" for="value">
@messages("produceApiShortDescription.title")
</label>
</h1>
<div id="with-hint-hint" class="govuk-hint">
@messages("produceApiShortDescription.hint")
</div>
}

@layout(pageTitle = title(form, messages("produceApiShortDescription.title")), user = Some(userModel)) {

@layout(pageTitle = titleNoForm("Enter a short description for your API")) {
@formHelper(action = controllers.myapis.produce.routes.ProduceApiShortDescriptionController.onSubmit(mode)) {
<h1 class="govuk-heading-l">Enter a short description for your API</h1>

@if(form.errors.nonEmpty) {
@govukErrorSummary(ErrorSummaryViewModel(form))
}

@govukCharacterCount(
CharacterCount(
id = form("value").id,
name = form("value").id,
maxLength = Some(180),
value = form("value").value,
hint = Some(Hint(
content = HtmlContent(hintContent())
))
)
)

@govukButton(
ButtonViewModel(messages("site.continue"))
)
Expand Down
7 changes: 7 additions & 0 deletions conf/messages.en
Original file line number Diff line number Diff line change
Expand Up @@ -874,3 +874,10 @@ produceApiChooseTeam.hint.link = create a new team
produceApiChooseTeam.checkYourAnswersLabel = Owning Team
produceApiChooseTeam.error.required = Please select an owning team
produceApiChooseTeam.change.hidden = ProduceApiChooseTeam

produceApiShortDescription.title = Enter a short description for your API
produceApiShortDescription.hint = The short description will be used to help identify your API, and will be displayed on the Explore APIs page.
produceApiShortDescription.checkYourAnswersLabel = Short API description
produceApiShortDescription.error.required = Enter a short API description
produceApiShortDescription.error.length = The API short description must be between 8 and 180 characters
produceApiShortDescription.change.hidden = ProduceApiShortDescription
9 changes: 7 additions & 2 deletions test-utils/generators/Generators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ trait Generators extends UserAnswersGenerator with PageGenerators with ModelGene
def nonEmptyString: Gen[String] =
arbitrary[String] suchThat (_.nonEmpty)

def stringsWithMaxLength(maxLength: Int): Gen[String] =
def stringsWithMaxLength(maxLength: Int, minLength: Int = 1): Gen[String] =
for {
length <- choose(1, maxLength)
length <- choose(minLength, maxLength)
chars <- listOfN(length, arbitrary[Char])
} yield chars.mkString

Expand All @@ -96,6 +96,11 @@ trait Generators extends UserAnswersGenerator with PageGenerators with ModelGene
chars <- listOfN(length, arbitrary[Char])
} yield chars.mkString

def stringsShorterThan(maxLength: Int): Gen[String] = for {
length <- Gen.chooseNum(1, maxLength - 1)
chars <- listOfN(length, arbitrary[Char])
} yield chars.mkString

def stringsExceptSpecificValues(excluded: Seq[String]): Gen[String] =
nonEmptyString suchThat (!excluded.contains(_))

Expand Down
3 changes: 3 additions & 0 deletions test-utils/generators/PageGenerators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import pages.myapis.produce.*

trait PageGenerators {

implicit lazy val arbitraryProduceApiShortDescriptionPage: Arbitrary[ProduceApiShortDescriptionPage.type] =
Arbitrary(ProduceApiShortDescriptionPage)

implicit lazy val arbitraryProduceApiChooseTeamPage: Arbitrary[ProduceApiChooseTeamPage.type] =
Arbitrary(ProduceApiChooseTeamPage)

Expand Down
8 changes: 8 additions & 0 deletions test-utils/generators/UserAnswersEntryGenerators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ import play.api.libs.json.{JsValue, Json}

trait UserAnswersEntryGenerators extends PageGenerators with ModelGenerators with TeamGenerator {

implicit lazy val arbitraryProduceApiShortDescriptionUserAnswersEntry: Arbitrary[(ProduceApiShortDescriptionPage.type, JsValue)] =
Arbitrary {
for {
page <- arbitrary[ProduceApiShortDescriptionPage.type]
value <- arbitrary[String].suchThat(_.nonEmpty).map(Json.toJson(_))
} yield (page, value)
}

implicit lazy val arbitraryProduceApiChooseTeamUserAnswersEntry: Arbitrary[(ProduceApiChooseTeamPage.type, JsValue)] =
Arbitrary {
for {
Expand Down
1 change: 1 addition & 0 deletions test-utils/generators/UserAnswersGenerator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ trait UserAnswersGenerator extends TryValues {
self: Generators =>

val generators: Seq[Gen[(QuestionPage[?], JsValue)]] =
arbitrary[(ProduceApiShortDescriptionPage.type, JsValue)] ::
arbitrary[(ProduceApiChooseTeamPage.type, JsValue)] ::
arbitrary[(CancelAccessRequestConfirmPage.type, JsValue)] ::
arbitrary[(RequestProductionAccessSelectApisPage.type, JsValue)] ::
Expand Down
Loading

0 comments on commit af55e90

Please sign in to comment.