Skip to content

Commit

Permalink
Merge pull request #987 from hmrc/BDOG-3332
Browse files Browse the repository at this point in the history
BDOG-3332 Add team and digital filter to shutter overview
  • Loading branch information
colin-lamed authored Jan 9, 2025
2 parents 385c560 + 489051f commit 16bfde2
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 121 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package uk.gov.hmrc.cataloguefrontend.shuttering
import javax.inject.{Inject, Singleton}
import play.api.libs.json.{Json, JsValue, JsNull, JsString, Reads, Writes}
import play.api.libs.ws.writeableOf_JsValue
import uk.gov.hmrc.cataloguefrontend.model.{Environment, ServiceName}
import uk.gov.hmrc.cataloguefrontend.model.{DigitalService, Environment, ServiceName, TeamName}
import uk.gov.hmrc.cataloguefrontend.shuttering.ShutterConnector.ShutterEventsFilter
import uk.gov.hmrc.http.{HeaderCarrier, HttpReads, StringContextOps, UpstreamErrorResponse}
import uk.gov.hmrc.http.client.HttpClientV2
Expand All @@ -45,15 +45,17 @@ class ShutterConnector @Inject() (
* Retrieves the current shutter states for all services in given environment
*/
def shutterStates(
st : ShutterType,
env : Environment,
serviceName: Option[ServiceName] = None
st : ShutterType,
env : Environment,
teamName : Option[TeamName],
digitalService: Option[DigitalService],
serviceName : Option[ServiceName]
)(using
HeaderCarrier
): Future[Seq[ShutterState]] =
given Reads[ShutterState] = ShutterState.reads
httpClientV2
.get(url"$baseUrl/shutter-api/${env.asString}/${st.asString}/states?serviceName=${serviceName.map(_.asString)}")
.get(url"$baseUrl/shutter-api/${env.asString}/${st.asString}/states?serviceName=${serviceName.map(_.asString)}&teamName=${teamName.map(_.asString)}&digitalService=${digitalService.map(_.asString)}")
.execute[Seq[ShutterState]]

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package uk.gov.hmrc.cataloguefrontend.shuttering
import javax.inject.{Inject, Singleton}
import play.api.Logger
import play.api.data.{Form, Forms}
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents, RequestHeader}
import uk.gov.hmrc.cataloguefrontend.auth.CatalogueAuthBuilders
import uk.gov.hmrc.cataloguefrontend.connector.RouteConfigurationConnector
import uk.gov.hmrc.cataloguefrontend.connector.RouteConfigurationConnector.RouteType
Expand Down Expand Up @@ -47,17 +47,17 @@ class ShutterEventsController @Inject() (
private val logger = Logger(getClass)

val shutterEvents: Action[AnyContent] =
Action {
Action:
Redirect(routes.ShutterEventsController.shutterEventsList(Environment.Production))
}

def shutterEventsList(
env : Environment,
serviceName: Option[ServiceName],
limit : Option[Int],
offset : Option[Int]
): Action[AnyContent] =
BasicAuthAction.async { implicit request =>
BasicAuthAction.async: request =>
given RequestHeader = request
val filter = filterFor(env, serviceName)
val form = ShutterEventsForm.fromFilter(filter)

Expand All @@ -72,7 +72,6 @@ class ShutterEventsController @Inject() (
Seq.empty
page = ShutterEventsPage(services, events, form, Environment.values.toSeq)
yield Ok(page)
}

private def filterFor(env: Environment, serviceNameOpt: Option[ServiceName]): ShutterEventsFilter =
ShutterEventsFilter(env, serviceNameOpt.filter(_.asString.trim.nonEmpty))
Expand All @@ -86,12 +85,11 @@ private case class ShutterEventsForm(

private object ShutterEventsForm:
lazy val form =
Form(
Form:
Forms.mapping(
"environment" -> Forms.of[Environment]
, "serviceName" -> Forms.optional(Forms.of[ServiceName])
)(ShutterEventsForm.apply)(f => Some(Tuple.fromProductTyped(f)))
)

def fromFilter(filter: ShutterEventsFilter): Form[ShutterEventsForm] =
form.fill(ShutterEventsForm(filter.environment, filter.serviceName))
Original file line number Diff line number Diff line change
Expand Up @@ -17,75 +17,106 @@
package uk.gov.hmrc.cataloguefrontend.shuttering

import cats.implicits._

import javax.inject.{Inject, Singleton}
import play.api.Logger
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents}
import play.api.data.{Form, Forms}
import play.api.mvc.{Action, AnyContent, MessagesControllerComponents, MessagesRequest, RequestHeader}
import uk.gov.hmrc.cataloguefrontend.auth.CatalogueAuthBuilders
import uk.gov.hmrc.cataloguefrontend.config.CatalogueConfig
import uk.gov.hmrc.cataloguefrontend.model.{Environment, ServiceName}
import uk.gov.hmrc.cataloguefrontend.connector.TeamsAndRepositoriesConnector
import uk.gov.hmrc.cataloguefrontend.model.{DigitalService, Environment, ServiceName, TeamName}
import uk.gov.hmrc.cataloguefrontend.shuttering.ShutterType.Rate
import uk.gov.hmrc.cataloguefrontend.shuttering.view.html.{FrontendRouteWarningsPage, ShutterOverviewPage}
import uk.gov.hmrc.internalauth.client.{FrontendAuthComponents, IAAction, Predicate, Resource, Retrieval}
import uk.gov.hmrc.play.bootstrap.frontend.controller.FrontendController

import javax.inject.{Inject, Singleton}
import scala.concurrent.ExecutionContext
import scala.util.control.NonFatal

@Singleton
class ShutterOverviewController @Inject() (
override val mcc : MessagesControllerComponents,
shutterOverviewPage : ShutterOverviewPage,
frontendRouteWarningPage : FrontendRouteWarningsPage,
shutterService : ShutterService,
catalogueConfig : CatalogueConfig,
override val auth : FrontendAuthComponents
override val mcc : MessagesControllerComponents,
shutterOverviewPage : ShutterOverviewPage,
frontendRouteWarningPage : FrontendRouteWarningsPage,
shutterService : ShutterService,
teamsAndRepositoriesConnector: TeamsAndRepositoriesConnector,
catalogueConfig : CatalogueConfig,
override val auth : FrontendAuthComponents
)(using
override val ec: ExecutionContext
) extends FrontendController(mcc)
with CatalogueAuthBuilders:

private val logger = Logger(getClass)

def allStates(shutterType: ShutterType): Action[AnyContent] =
def allStates(
shutterType : ShutterType,
teamName : Option[TeamName],
digitalService: Option[DigitalService]
): Action[AnyContent] =
allStatesForEnv(
shutterType = shutterType,
env = Environment.Production
shutterType = shutterType,
env = Environment.Production,
teamName = teamName,
digitalService = digitalService
)

def allStatesForEnv(shutterType: ShutterType, env: Environment): Action[AnyContent] =
BasicAuthAction.async { implicit request =>
def allStatesForEnv(
shutterType : ShutterType,
env : Environment,
teamName : Option[TeamName],
digitalService: Option[DigitalService]
): Action[AnyContent] =
BasicAuthAction.async: request =>
given MessagesRequest[AnyContent] = request
for
teams <- teamsAndRepositoriesConnector.allTeams().map(_.map(_.name))
digitalServices <- teamsAndRepositoriesConnector.allDigitalServices()
envAndCurrentStates <- Environment.values.toSeq.traverse: env =>
shutterService
.findCurrentStates(shutterType, env)
.recover:
case NonFatal(ex) =>
logger.error(s"Could not retrieve currentState: ${ex.getMessage}", ex)
Seq.empty
.findCurrentStates(
shutterType,
env,
teamName.filter(_.asString.nonEmpty),
digitalService.filter(_.asString.nonEmpty)
)
.map(ws => (env, ws))
hasGlobalPerm <- auth
.verify:
Retrieval.hasPredicate(Predicate.Permission(Resource.from("shutter-api", "mdtp"), IAAction("SHUTTER")))
.map(_.exists(_ == true))
killSwitchLink = if hasGlobalPerm && shutterType != Rate then Some(catalogueConfig.killSwitchLink(shutterType.asString)) else None
page = shutterOverviewPage(envAndCurrentStates.toMap, shutterType, env, killSwitchLink)
yield Ok(page)
}
hasGlobalPerm <- auth
.verify:
Retrieval.hasPredicate(Predicate.Permission(Resource.from("shutter-api", "mdtp"), IAAction("SHUTTER")))
.map(_.exists(_ == true))
killSwitchLink = if hasGlobalPerm && shutterType != Rate then Some(catalogueConfig.killSwitchLink(shutterType.asString)) else None
yield Ok(shutterOverviewPage(
form.bindFromRequest(),
envAndCurrentStates.toMap,
shutterType,
env,
teamName,
digitalService,
killSwitchLink,
teams,
digitalServices
))

case class Filter(
team : Option[TeamName],
digitalService: Option[DigitalService]
)

lazy val form: Form[Filter] =
Form(
Forms.mapping(
"teamName" -> Forms.optional(Forms.of[TeamName]),
"digitalService" -> Forms.optional(Forms.of[DigitalService])
)(Filter.apply)(f => Some(Tuple.fromProductTyped(f)))
)


def frontendRouteWarnings(env: Environment, serviceName: ServiceName): Action[AnyContent] =
BasicAuthAction.async { implicit request =>
BasicAuthAction.async: request =>
given RequestHeader = request
for
envsAndWarnings <- Environment.values.toSeq.traverse: env =>
shutterService
.frontendRouteWarnings(env, serviceName)
.recover:
case NonFatal(ex) =>
logger.error(s"Could not retrieve frontend route warnings for service '${serviceName.asString}' in env: '${env.asString}': ${ex.getMessage}", ex)
Seq.empty
.map(ws => (env, ws))
page = frontendRouteWarningPage(envsAndWarnings.toMap, env, serviceName)
yield Ok(page)
}

end ShutterOverviewController
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import cats.implicits.*
import javax.inject.{Inject, Singleton}
import uk.gov.hmrc.cataloguefrontend.connector.{GitHubProxyConnector, RouteConfigurationConnector}
import uk.gov.hmrc.cataloguefrontend.connector.RouteConfigurationConnector.RouteType
import uk.gov.hmrc.cataloguefrontend.model.{Environment, ServiceName}
import uk.gov.hmrc.cataloguefrontend.model.{DigitalService, Environment, ServiceName, TeamName}
import uk.gov.hmrc.internalauth.client.AuthenticatedRequest
import uk.gov.hmrc.http.HeaderCarrier

Expand All @@ -46,7 +46,7 @@ class ShutterService @Inject() (
)(using
HeaderCarrier
): Future[Seq[ShutterState]] =
shutterConnector.shutterStates(st, env, serviceName)
shutterConnector.shutterStates(st, env, teamName = None, digitalService = None, serviceName)

def updateShutterStatus(
serviceName: ServiceName,
Expand Down Expand Up @@ -88,13 +88,15 @@ class ShutterService @Inject() (
shutterConnector.frontendRouteWarnings(env, serviceName)

def findCurrentStates(
st : ShutterType,
env: Environment
st : ShutterType,
env : Environment,
teamName : Option[TeamName],
digitalService: Option[DigitalService]
)(using
HeaderCarrier
): Future[Seq[(ShutterState, Option[ShutterStateChangeEvent])]] =
for
states <- shutterConnector.shutterStates(st, env)
states <- shutterConnector.shutterStates(st, env, teamName, digitalService, serviceName = None)
events <- shutterConnector.latestShutterEvents(st, env)
yield
states
Expand Down
Loading

0 comments on commit 16bfde2

Please sign in to comment.