Skip to content

Commit

Permalink
Merge pull request #41 from hmrc/BDOG-24-UrlSearch
Browse files Browse the repository at this point in the history
Bdog 24 url search
  • Loading branch information
IanMcShane committed Jan 15, 2019
2 parents f1b3955 + c03f0b3 commit 847bd67
Show file tree
Hide file tree
Showing 8 changed files with 300 additions and 5 deletions.
74 changes: 74 additions & 0 deletions app/uk/gov/hmrc/cataloguefrontend/SearchByUrlController.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright 2019 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.cataloguefrontend

import javax.inject.{Inject, Singleton}
import play.api.data.Form
import play.api.data.Forms.{mapping, optional, text}
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
import uk.gov.hmrc.cataloguefrontend.connector.RepoType
import uk.gov.hmrc.play.bootstrap.controller.FrontendController
import views.html.SearchByUrlPage

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import uk.gov.hmrc.cataloguefrontend.service.SearchByUrlService

@Singleton
class SearchByUrlController @Inject()(
mcc: MessagesControllerComponents,
searchByUrlService: SearchByUrlService,
searchByUrlPage: SearchByUrlPage
) extends FrontendController(mcc) {

private val serviceNameToUrl = routes.CatalogueController.service _

def searchLanding: Action[AnyContent] = Action.async { implicit request =>
Future.successful(Ok(searchByUrlPage(UrlSearchFilter.form, Nil, serviceNameToUrl)))
}

def searchUrl = Action.async { implicit request =>
UrlSearchFilter.form
.bindFromRequest()
.fold(
formWithErrors => Future.successful(Ok(searchByUrlPage(formWithErrors, Nil, serviceNameToUrl))),
query => {
for {
searchResult <- searchByUrlService.search(query.name)
} yield searchResult match {
case _ =>
Ok(searchByUrlPage(
UrlSearchFilter.form.bindFromRequest(),
searchResult,
serviceNameToUrl))
}
}
)
}

case class UrlSearchFilter(name: Option[String] = None) {
def isEmpty: Boolean = name.isEmpty
}

object UrlSearchFilter {
lazy val form = Form(
mapping(
"name" -> optional(text).transform[Option[String]](x => if (x.exists(_.trim.isEmpty)) None else x, identity)
)(UrlSearchFilter.apply)(UrlSearchFilter.unapply)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2019 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.cataloguefrontend.connector

import javax.inject.{Inject, Singleton}
import play.api.Logger
import play.api.libs.json.{Json, Reads}
import uk.gov.hmrc.cataloguefrontend.service.SearchByUrlService.{FrontendRoute, FrontendRoutes}
import uk.gov.hmrc.http.HeaderCarrier
import uk.gov.hmrc.play.bootstrap.config.ServicesConfig
import uk.gov.hmrc.play.bootstrap.http.HttpClient

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.control.NonFatal

@Singleton
class SearchByUrlConnector @Inject()(
http: HttpClient,
servicesConfig: ServicesConfig
) {

private val url: String = s"${servicesConfig.baseUrl("service-configs")}/frontend-route/search"

implicit val frontendRouteReads: Reads[FrontendRoute] = Json.using[Json.WithDefaultValues].reads[FrontendRoute]
implicit val frontendRoutesReads: Reads[FrontendRoutes] = Json.reads[FrontendRoutes]

def search(term: String)(implicit hc: HeaderCarrier): Future[Seq[FrontendRoutes]] =
http
.GET[Seq[FrontendRoutes]](s"$url", Seq("frontendPath" -> term))
.recover {
case NonFatal(ex) =>
Logger.error(s"An error occurred when connecting to $url: ${ex.getMessage}", ex)
Seq.empty
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,17 @@ import javax.inject.{Inject, Singleton}
import uk.gov.hmrc.cataloguefrontend.connector.RouteRulesConnector
import uk.gov.hmrc.http.HeaderCarrier
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

class RouteRulesService @Inject()(routeRulesConnector: RouteRulesConnector) {
import RouteRulesService._

def serviceRoutes(serviceName: String)(implicit hc: HeaderCarrier) =
def serviceRoutes(serviceName: String)(implicit hc: HeaderCarrier): Future[ServiceRoutes] =
routeRulesConnector.serviceRoutes(serviceName).map(environmentRoutes =>
ServiceRoutes(environmentRoutes)
)

def serviceUrl(serviceName: String, environment: String = "production")(implicit hc: HeaderCarrier) =
def serviceUrl(serviceName: String, environment: String = "production")(implicit hc: HeaderCarrier): Future[Option[EnvironmentRoute]] =
routeRulesConnector.serviceRoutes(serviceName).map(environmentRoutes => {
environmentRoutes
.find(environmentRoute => environmentRoute.environment == environment)
Expand Down
75 changes: 75 additions & 0 deletions app/uk/gov/hmrc/cataloguefrontend/service/SearchByUrlService.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright 2019 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.cataloguefrontend.service

import java.net.{URI, URISyntaxException}
import javax.inject._
import uk.gov.hmrc.cataloguefrontend.connector.SearchByUrlConnector
import uk.gov.hmrc.http.HeaderCarrier
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

@Singleton
class SearchByUrlService @Inject()(searchByUrlConnector: SearchByUrlConnector) {

import SearchByUrlService._

def search(term: Option[String], environment: String = "production")(implicit hc: HeaderCarrier): Future[Seq[FrontendRoutes]] =
if (isValidSearchTerm(term)) {
searchByUrlConnector
.search(takeUrlPath(term.get))
.map(results => results.filter(frontendRoute => frontendRoute.environment == environment))
}
else {
Future.successful(Nil)
}

private def isValidSearchTerm(term: Option[String]): Boolean = {
if (term.isEmpty || term.getOrElse("").trim.isEmpty || term.getOrElse("").trim == "/")
return false

try {
val url = new URI(term.get)

Option(url.getPath).getOrElse("").nonEmpty &&
(!Option(url.getPath).getOrElse("").contains("tax.service.gov.uk") ||
Option(url.getHost).getOrElse("").isEmpty &&
Option(url.getPath).getOrElse("").contains("tax.service.gov.uk") &&
url.getPath.substring(url.getPath.indexOf(".gov.uk") + 7).trim.nonEmpty)
} catch {
case e: URISyntaxException => false
}
}

private def takeUrlPath(term: String): String = {
val url = new URI(term)

if (Option(url.getHost).getOrElse("").trim.nonEmpty)
return url.getPath.trim

if (Option(url.getHost).getOrElse("").trim.isEmpty &&
Option(url.getPath).getOrElse("").contains("tax.service.gov.uk"))
return url.getPath.substring(url.getPath.indexOf(".gov.uk") + 7).trim

url.getPath.trim
}
}

object SearchByUrlService {
case class FrontendRoute(frontendPath: String, backendPath :String, ruleConfigurationUrl: String = "", isRegex: Boolean = false)
case class FrontendRoutes(service: String, environment: String, routes: Seq[FrontendRoute])
}
90 changes: 90 additions & 0 deletions app/views/SearchByUrlPage.scala.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
@*
* Copyright 2019 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.
*@

@import views.html.helper.CSRF
@import play.api.mvc.Call
@import uk.gov.hmrc.cataloguefrontend.ViewMessages
@import uk.gov.hmrc.cataloguefrontend.service.SearchByUrlService.FrontendRoutes

@this(viewMessages: ViewMessages)
@(form: Form[_],
searchResults: Seq[FrontendRoutes],
serviceNameToUrl: String => Call)(implicit request: Request[_])

@standard_layout("Service Search", "search") {
<header>
<h1 id="search-service-header">Service Search</h1>
</header>
<div id="service-list">
<div class="board">
<h3 id="info-box-header" class="board__heading"><img class="info-icon" src="assets/infoicon.svg"/> How to find a service</h3>
<div class="board__body">
<p id="info-box-copy">Use this page to search for a service on the tax platform. Enter the URL into the box below and click on the service name to find out more about it.</p>
<div class="row col-xs-12 ">
<p>Addresses beginning with the following are not tax platform services:</p>
<ul class="list list--minimal">
<li class="list-item">https://www.access.tax.service.gov.uk/</li>
<li class="list-item">https://www.gov.uk/</li>
</ul>
</div>
</div>
</div>
<div class="board">
<div class="board__body">
<form action="/search" method="post">
@{
play.filters.csrf.CSRF.getToken(request).map { token => CSRF.formField(request) }
}
<div class="form-group row col-xs-12">
<div class="col-xs-3"><label for="search">https://www.tax.service.gov.uk/</label></div>
<div class="col-xs-8"><input class="search form-control" id="search-box" type="text" name="name" value="@form("name").value" autofocus /></div>
<div class="col-xs-1"><button id="search-button" class="btn btn-primary" type="submit">Search</button></div>
</div>
</form>
@if(searchResults.nonEmpty) {
<table id="search-results" class="table table-striped">
<tr>
<th class="col-lg-3">Service</th>
<th class="col-lg-9">URL</th>
</tr>
<tbody class="list">
@for(result <- searchResults) {
@for(route <- result.routes) {
<tr>
<td><a target="_blank" href="@{
serviceNameToUrl(result.service)
}">@result.service</a>
</td>
<td>
@if(route.isRegex) {
@route.frontendPath
} else {
<a href="https://[email protected]" target="_blank">@route.frontendPath <span class="glyphicon glyphicon-new-window"/></a>
}
</td>
</tr>
}
}
</tbody>
</table>
}
@if(searchResults.isEmpty && form("name").value.nonEmpty) {
<p id="search-results-empty">This search did not return any results.</p>
}
</div>
</div>
</div>
}
3 changes: 0 additions & 3 deletions app/views/repository_list.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@ <h1>@pageTitle</h1>
</tr>
}
</tbody>


</table>

</div>
}
3 changes: 3 additions & 0 deletions conf/app.routes
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ GET /dependencyReport uk.gov.hmrc.cataloguefro
GET /sign-in @uk.gov.hmrc.cataloguefrontend.AuthController.showSignInPage
POST /sign-in @uk.gov.hmrc.cataloguefrontend.AuthController.submit
GET /sign-out @uk.gov.hmrc.cataloguefrontend.AuthController.signOut

GET /search uk.gov.hmrc.cataloguefrontend.SearchByUrlController.searchLanding
POST /search uk.gov.hmrc.cataloguefrontend.SearchByUrlController.searchUrl
5 changes: 5 additions & 0 deletions conf/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ controllers {
needsAuditing = false
}

uk.gov.hmrc.cataloguefrontend.SearchByUrlController = {
needsAuth = false
needsLogging = false
needsAuditing = false
}
}


Expand Down

0 comments on commit 847bd67

Please sign in to comment.