Skip to content

Commit

Permalink
Merge pull request #66 from hmrc/BDOG-183
Browse files Browse the repository at this point in the history
Bdog 183
  • Loading branch information
colin-lamed authored May 7, 2019
2 parents 81584bc + 7f0f521 commit 25771c4
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 42 deletions.
16 changes: 9 additions & 7 deletions app/uk/gov/hmrc/cataloguefrontend/BobbyExplorerController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ import views.html.BobbyExplorerPage

import scala.concurrent.ExecutionContext

class BobbyExplorerController @Inject() (mcc : MessagesControllerComponents,
page : BobbyExplorerPage,
bobbyService: BobbyService
)( implicit val ec: ExecutionContext) extends FrontendController(mcc) {
def list(): Action[AnyContent] = Action.async { implicit request =>
bobbyService.getRules().map(r => Ok(page(r)))
}
class BobbyExplorerController @Inject()(
mcc : MessagesControllerComponents,
page : BobbyExplorerPage,
bobbyService: BobbyService
)(implicit val ec: ExecutionContext
) extends FrontendController(mcc) {
def list(): Action[AnyContent] = Action.async { implicit request =>
bobbyService.getRules().map(r => Ok(page(r)))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,8 @@ class ConfigConnector @Inject()(
def configByKey(service: String)(implicit hc: HeaderCarrier) =
http.GET[ConfigByKey](s"$serviceConfigsBaseUrl/config-by-key/$service")

def bobbyRules()(implicit hc: HeaderCarrier): Future[BobbyRuleSet] = http.GET[BobbyRuleSet](s"$serviceConfigsBaseUrl/bobby/rules")
def bobbyRules()(implicit hc: HeaderCarrier): Future[BobbyRuleSet] = {
implicit val brsr = BobbyRuleSet.reads
http.GET[BobbyRuleSet](s"$serviceConfigsBaseUrl/bobby/rules")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,24 @@ import java.time.LocalDate

import play.api.libs.json.{Json, Reads}

case class BobbyRule(organisation: String, name: String, range: String, reason: String, from: LocalDate) {
case class BobbyRule(organisation: String, name: String, range: BobbyVersionRange, reason: String, from: LocalDate) {
val groupArtifactName: String = {
val wildcard = "*"
if (organisation == wildcard && name == wildcard) "*" else s"$organisation:$name"
}
}

object BobbyRule {
implicit val reader: Reads[BobbyRule] = Json.reads[BobbyRule]

val reads: Reads[BobbyRule] = {
implicit val bvrr = BobbyVersionRange.reads
Json.reads[BobbyRule]
}
}

case class BobbyRuleSet(libraries: Seq[BobbyRule], plugins: Seq[BobbyRule])
object BobbyRuleSet {
implicit val reader: Reads[BobbyRuleSet] = Json.reads[BobbyRuleSet]
val reads: Reads[BobbyRuleSet] = {
implicit val brr = BobbyRule.reads
Json.reads[BobbyRuleSet]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package uk.gov.hmrc.cataloguefrontend.connector.model
import java.time.LocalDate

import org.joda.time.DateTime
import play.api.libs.json.{Format, JsError, JsObject, JsString, JsSuccess, JsValue, Json, OFormat, Reads, __}
import play.api.libs.json.{Format, JsError, JsObject, JsPath, JsString, JsSuccess, JsValue, Json, OFormat, Reads, __}
import play.api.libs.functional.syntax._
import uk.gov.hmrc.http.controllers.RestFormats

Expand All @@ -33,11 +33,6 @@ object VersionState {
case object BobbyRulePending extends VersionState
}

// TODO avoid caching LocalDate, and provide to isActive function
case class BobbyRuleViolation(reason: String, range: String, from: LocalDate)(implicit now: LocalDate = LocalDate.now()) {
def isActive: Boolean = now.isAfter(from)
}

case class Dependency(
name : String,
currentVersion: Version,
Expand Down Expand Up @@ -74,12 +69,83 @@ case class Dependencies(
toSeq.exists(_.isOutOfDate)
}

case class BobbyVersion(version: Version, inclusive: Boolean)

case class BobbyVersionRange(
lowerBound: Option[BobbyVersion]
, upperBound: Option[BobbyVersion]
, qualifier : Option[String]
, range : String
) {

def rangeDescr: Option[(String, String)] = {
def comp(v: BobbyVersion) = if (v.inclusive) " <= " else " < "
if (lowerBound.isDefined || upperBound.isDefined) {
Some(( lowerBound.map(v => s"${v.version} ${comp(v)}").getOrElse("")
, upperBound.map(v => s"${comp(v)} ${v.version}").getOrElse("")
))
} else None
}
}

object BobbyVersionRange {

private val fixed = """^\[(\d+\.\d+.\d+)\]""".r
private val fixedUpper = """^\(,?(\d+\.\d+.\d+)[\]\)]""".r
private val fixedLower = """^[\[\(](\d+\.\d+.\d+),[\]\)]""".r
private val rangeRegex = """^[\[\(](\d+\.\d+.\d+),(\d+\.\d+.\d+)[\]\)]""".r
private val qualifier = """^\[[-\*]+(.*)\]""".r

def apply(range: String): BobbyVersionRange = {
val trimmedRange = range.replaceAll(" ", "")

trimmedRange match {
case fixed(v) =>
val fixed = Version.parse(v).map(BobbyVersion(_, inclusive = true))
BobbyVersionRange(lowerBound = fixed, upperBound = fixed, qualifier = None, range = trimmedRange)
case fixedUpper(v) =>
BobbyVersionRange(
lowerBound = None,
upperBound = Version.parse(v).map(BobbyVersion(_, inclusive = trimmedRange.endsWith("]"))),
qualifier = None,
range = trimmedRange)
case fixedLower(v) =>
BobbyVersionRange(
lowerBound = Version.parse(v).map(BobbyVersion(_, inclusive = trimmedRange.startsWith("["))),
upperBound = None,
qualifier = None,
range = trimmedRange)
case rangeRegex(v1, v2) =>
BobbyVersionRange(
lowerBound = Version.parse(v1).map(BobbyVersion(_, inclusive = trimmedRange.startsWith("["))),
upperBound = Version.parse(v2).map(BobbyVersion(_, inclusive = trimmedRange.endsWith("]"))),
qualifier = None,
range = trimmedRange
)
case qualifier(q) if q.length() > 1 =>
BobbyVersionRange(lowerBound = None, upperBound = None, qualifier = Some(q), range = trimmedRange)
case _ => BobbyVersionRange(lowerBound = None, upperBound = None, qualifier = None, range = trimmedRange)
}
}

val reads: Reads[BobbyVersionRange] =
JsPath.read[String].map(BobbyVersionRange.apply)

}

// TODO avoid caching LocalDate, and provide to isActive function
case class BobbyRuleViolation(reason: String, range: BobbyVersionRange, from: LocalDate)(implicit now: LocalDate = LocalDate.now()) {
def isActive: Boolean = now.isAfter(from)
}


object Dependencies {
val reads: Reads[Dependencies] = {
implicit val dtr = RestFormats.dateTimeFormats
implicit val bvr = BobbyVersionRange.reads
implicit val brvf =
( (__ \ "reason" ).read[String]
~ (__ \ "range" ).read[JsValue].map(j => (j \ "range").as[String])
~ (__ \ "range" ).read[BobbyVersionRange]
~ (__ \ "from" ).read[LocalDate]
)(BobbyRuleViolation.apply _)

Expand Down
27 changes: 22 additions & 5 deletions app/views/BobbyExplorerPage.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ <h1>Bobby Rules</h1>
<thead>
<tr>
<th>@artifactType</th>
<th>Version</th>
<th colspan="3">Banned Versions</th>
<th>Reason</th>
<th>Active from</th>
</tr>
Expand All @@ -57,12 +57,29 @@ <h1>Bobby Rules</h1>
</div>
}
}

@bobbyRuleRow(rule: BobbyRule) = {
<tr class="bobby-rule">
<td>@rule.groupArtifactName</td>
<td>@rule.range</td>
<td><a name="@rule.name">@rule.groupArtifactName</a></td>
@defining(rule.range.rangeDescr) { rangeDescr =>
@rangeDescr match {
case Some((lbDescr, upDescr)) => {
<td data-toggle="tooltip" title="@rule.range.range" style="white-space: nowrap; padding-right: 0; text-align: right">@lbDescr</td>
<td data-toggle="tooltip" title="@rule.range.range">x</td>
<td data-toggle="tooltip" title="@rule.range.range" style="white-space: nowrap; padding-left: 0; text-align: left">@upDescr</td>
}
case None => {
<td colspan="3">@rule.range.range</td>
}
}
}
<td>@rule.reason</td>
<td>@rule.from</td>
</tr>
}
}

<script type="text/javascript">
ready(function() {
document.querySelector('[data-toggle="tooltip"]').tooltip();
});
</script>
3 changes: 1 addition & 2 deletions app/views/partials/DependenciesPartial.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,8 @@ <h3 class="board__heading">Platform Dependencies</h3>
<li>
<span class="col-xs-4"><label>Libraries</label></span>
<span class="col-xs-3"><label>Current Version</label></span>
<span class="col-xs-1"><label> </label></span>
<span class="col-xs-3"><label>Latest Version</label></span>

<span class="col-xs-2"><label>Bobby Rule Violation</label></span>
</li>
@partials.dependency_section(mayBeDependencies.get.libraryDependencies)

Expand Down
2 changes: 1 addition & 1 deletion app/views/partials/DependenciesTeamPartial.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ <h4>@dependency.repositoryName</h4>
<li>
<span class="col-xs-4"><label>Dependency</label></span>
<span class="col-xs-3"><label>Current Version</label></span>
<span class="col-xs-1"><label> </label></span>
<span class="col-xs-3"><label>Latest Version</label></span>
<span class="col-xs-2"><label>Bobby Rule Violation</label></span>

</li>
@partials.dependency_section(dependency.libraryDependencies.filter(_.isOutOfDate))
Expand Down
12 changes: 10 additions & 2 deletions app/views/partials/dependency_section.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*@

@import uk.gov.hmrc.cataloguefrontend.connector.model.{Dependency, VersionState}
@import uk.gov.hmrc.cataloguefrontend.{ routes => appRoutes }
@(dependencies: Seq[Dependency])


Expand All @@ -30,8 +31,15 @@
}
</span>
<span id="@{dependency.name}-current-version" class="col-xs-3">@dependency.currentVersion.toString</span>
<span class="col-xs-1 glyphicon glyphicon-arrow-right small-glyphicon"> </span>
<span id="@{dependency.name}-latestVersion-version" class="col-xs-3">@dependency.latestVersion.map(_.toString).getOrElse("(not found)")</span>
<span id="@{dependency.name}-latestVersion-version" class="col-xs-3">
<span class="glyphicon glyphicon-arrow-right small-glyphicon" style="padding-right: 10;"> </span>
@dependency.latestVersion.map(_.toString).getOrElse("(not found)")
</span>
<span id="@{dependency.name}-bobbyrule" class="col-xs-2">
@if(dependency.versionState.fold(false)(v => List(VersionState.BobbyRuleViolated, VersionState.BobbyRulePending).contains(v))) {
<a href="@{appRoutes.BobbyExplorerController.list()}#@{dependency.name}">See rule</a>
} else { }
</span>
</div>
</li>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ import java.time.LocalDate

object BobbyRuleFactory {
def aBobbyRule(organisation: String = "uk.gov.hmrc", name: String = "play-frontend", range: String = "[*-SNAPSHOT]", reason: String = "No snapshot dependencies permitted", from: LocalDate = LocalDate.of(2015, 3, 16)) =
BobbyRule(organisation, name, range, reason, from)
BobbyRule(organisation, name, BobbyVersionRange(range), reason, from)
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,15 @@ class DependencySpec extends FreeSpec with Matchers {

"should return BobbyRuleViolated if dependency has any broken bobby rules" in {
Dependency("library-abc", Version("1.2.3"), Some(Version("2.2.3")),
Seq(BobbyRuleViolation("banned library", "1.2.3", LocalDate.of(1,1,1)))).versionState shouldBe Some(VersionState.BobbyRuleViolated)
Seq(BobbyRuleViolation("banned library", BobbyVersionRange("1.2.3"), LocalDate.of(1,1,1)))).versionState shouldBe Some(VersionState.BobbyRuleViolated)
}

"should return BobbyRulePending if dependency will break future rules" in {
new Dependency(
"library-abc"
, Version("1.2.3")
, Some(Version("2.2.3"))
, Seq(BobbyRuleViolation("banned library", "1.2.3", LocalDate.of(9999,1,1))(now = LocalDate.of(200,1,2)))
, Seq(BobbyRuleViolation("banned library", BobbyVersionRange("1.2.3"), LocalDate.of(9999,1,1))(now = LocalDate.of(200,1,2)))
)
.versionState shouldBe Some(VersionState.BobbyRulePending)
}
Expand All @@ -64,8 +64,8 @@ class DependencySpec extends FreeSpec with Matchers {
"library-abc"
, Version("1.2.3")
, Some(Version("2.2.3"))
, Seq( BobbyRuleViolation("banned library", "1.2.3", LocalDate.of(9999,1,1))(now = LocalDate.of(2000,1,2))
, BobbyRuleViolation("banned library", "1.2.3", LocalDate.of(1,1,1))(now = LocalDate.of(2000,1,2))
, Seq( BobbyRuleViolation("banned library", BobbyVersionRange("1.2.3"), LocalDate.of(9999,1,1))(now = LocalDate.of(2000,1,2))
, BobbyRuleViolation("banned library", BobbyVersionRange("1.2.3"), LocalDate.of(1,1,1))(now = LocalDate.of(2000,1,2))
)
).versionState shouldBe Some(VersionState.BobbyRuleViolated)
}
Expand All @@ -87,14 +87,14 @@ class DependencySpec extends FreeSpec with Matchers {
"library-abc"
, Version("1.2.3")
, Some(Version("2.2.3"))
, Seq(BobbyRuleViolation("banned library", "1.2.3", LocalDate.of(1,1,1))(now = LocalDate.of(2000,1,2)))
, Seq(BobbyRuleViolation("banned library", BobbyVersionRange("1.2.3"), LocalDate.of(1,1,1))(now = LocalDate.of(2000,1,2)))
)

val pendingDep = new Dependency(
"library-xyz"
, Version("1.2.3")
, Some(Version("2.2.3"))
, Seq(BobbyRuleViolation("banned library", "1.2.3", LocalDate.of(9999,1,1))(now = LocalDate.of(2000,1,2)))
, Seq(BobbyRuleViolation("banned library", BobbyVersionRange("1.2.3"), LocalDate.of(9999,1,1))(now = LocalDate.of(2000,1,2)))
)

val goodDep = Dependency("library-lol", Version("1.2.3"), Some(Version("2.2.3")))
Expand All @@ -115,14 +115,14 @@ class DependencySpec extends FreeSpec with Matchers {
"library-abc"
, Version("1.2.3")
, Some(Version("2.2.3"))
, Seq(BobbyRuleViolation("banned library", "1.2.3", LocalDate.of(1,1,1))(now = LocalDate.of(2000,1,2)))
, Seq(BobbyRuleViolation("banned library", BobbyVersionRange("1.2.3"), LocalDate.of(1,1,1))(now = LocalDate.of(2000,1,2)))
)

val pendingDep = new Dependency(
"library-xyz"
, Version("1.2.3")
, Some(Version("2.2.3"))
, Seq(BobbyRuleViolation("banned library", "1.2.3", LocalDate.of(9999,1,1))(now = LocalDate.of(2000,1,2)))
, Seq(BobbyRuleViolation("banned library", BobbyVersionRange("1.2.3"), LocalDate.of(9999,1,1))(now = LocalDate.of(2000,1,2)))
)

val goodDep = Dependency("library-lol", Version("1.2.3"), Some(Version("2.2.3")))
Expand Down
14 changes: 10 additions & 4 deletions test/view/partials/DependencySectionSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,20 @@ class DependencySectionSpec extends WordSpec with Matchers {

"display the version suffix when present" in {
val res = views.html.partials.dependency_section(Seq(dependency1)).body
res should include ("<span id=\"example-library-current-version\" class=\"col-xs-3\">1.2.3-play-25</span>")
res should include ("<span id=\"example-library-latestVersion-version\" class=\"col-xs-3\">1.2.3-play-26</span>")
res should include ("""<span id="example-library-current-version" class="col-xs-3">1.2.3-play-25</span>""")
res should include ("""<span id="example-library-latestVersion-version" class="col-xs-3">
| <span class="glyphicon glyphicon-arrow-right small-glyphicon" style="padding-right: 10;"> </span>
| 1.2.3-play-26
| </span>""".stripMargin)
}

"not display the version suffix when missing" in {
val res = views.html.partials.dependency_section(Seq(dependency2)).body
res should include ("<span id=\"library4j-current-version\" class=\"col-xs-3\">4.0.1</span>")
res should include ("<span id=\"library4j-latestVersion-version\" class=\"col-xs-3\">4.2.0</span>")
res should include ("""<span id="library4j-current-version" class="col-xs-3">4.0.1</span>""")
res should include ("""<span id="library4j-latestVersion-version" class="col-xs-3">
| <span class="glyphicon glyphicon-arrow-right small-glyphicon" style="padding-right: 10;"> </span>
| 4.2.0
| </span>""".stripMargin)
}
}

Expand Down

0 comments on commit 25771c4

Please sign in to comment.