
com.twitter.server.handler.LintHandler.scala Maven / Gradle / Ivy
package com.twitter.server.handler
import com.twitter.finagle.Service
import com.twitter.finagle.http.{Response, Request}
import com.twitter.io.Buf
import com.twitter.server.handler.LintHandler.LintView
import com.twitter.server.util.HttpUtils._
import com.twitter.server.util.JsonConverter
import com.twitter.util.Future
import com.twitter.util.lint.{Issue, Rule, GlobalRules}
/**
* UI for running the globally registered lint [[Rule Rules]].
*/
class LintHandler extends Service[Request, Response] {
def apply(req: Request): Future[Response] =
if (expectsHtml(req)) htmlResponse(req) else jsonResponse(req)
private[this] def htmlResponse(req: Request): Future[Response] = {
// first, run the rules.
val rules = GlobalRules.get.iterable.toSeq
val (oks, nots) = rules
.map(rule => rule -> rule())
.partition { case (_, res) => res.isEmpty }
// render the ui and respond
val view = new LintView(rules, oks.map(o => o._1), nots)
val rendered = view()
newResponse(
// note: contentType must be explicit here because of `IndexView.isFragment`
contentType = "text/html;charset=UTF-8",
content = Buf.Utf8(rendered)
)
}
private[this] def jsonString(s: String): String =
s.replace("\n", " ").trim
private[this] def jsonRule(rule: Rule): Map[String, Any] =
Map(
"category" -> rule.category.toString,
"id" -> rule.id,
"name" -> rule.name,
"description" -> jsonString(rule.description)
)
private[this] def jsonResponse(req: Request): Future[Response] = {
val rules = GlobalRules.get.iterable.toSeq
val jsonMap = jsonMapFromRules(rules)
newOk(JsonConverter.writeToString(jsonMap))
}
/** exposed for testing */
private[handler] def jsonMapFromRules(rules: Seq[Rule]): Map[String, Any] = {
val (oks, nots) = rules
.map(rule => rule -> rule())
.partition { case (_, res) => res.isEmpty }
val numIssues = nots.foldLeft(0) { case (total, (_, issues)) =>
total + issues.size
}
val failureIssues = nots.map { case (rule, issues) =>
jsonRule(rule) ++
Map("issues" -> issues.map(i => jsonString(i.details)))
}
val okRules = oks.map { case (rule, _) =>
rule.id
}
Map("lint_results" ->
Map(
"metadata" ->
Map(
"num_rules_run" -> rules.size,
"num_rules_ok" -> oks.size,
"num_rules_failed" -> nots.size,
"num_issues_found" -> numIssues
),
"failed_rules" -> failureIssues,
"ok_rules" -> okRules
)
)
}
}
private object LintHandler {
/**
* Web UI for [[LintHandler]].
*/
class LintView(
rules: Seq[Rule],
oks: Seq[Rule],
nots: Seq[(Rule, Seq[Issue])])
{
def apply(): String = {
scriptHeader +
summary +
failedRows +
okRows
}
private def scriptHeader: String =
"""
"""
private def escapeHtml(s: String): String =
xml.Utility.escape(s)
def summary: String = {
val numIssues = nots.foldLeft(0) { case (total, (_, issues)) =>
total + issues.size
}
s"""
Summary
Number of rules run
${rules.size}
Number of rules ok
${oks.size}
Number of rules failed
${nots.size}
Number of issues found
$numIssues
"""
}
def nameWithDescription(rule: Rule): String = {
val desc = escapeHtml(rule.description)
s"""
${escapeHtml(rule.name)}
"""
}
def failedRow(rule: Rule, issue: Issue): String = {
val desc = escapeHtml(rule.description)
s"""
${nameWithDescription(rule)}
${escapeHtml(issue.details)}
"""
}
def failedRows: String = {
val data = nots.map { case (rule, issues) =>
issues.map { issue =>
failedRow(rule, issue)
}.mkString("")
}.mkString("")
s"""
Failed rules
Name
Issue
$data
"""
}
def okRows: String = {
val data = oks.map { rule =>
s"""
${nameWithDescription(rule)}
"""
}.mkString("")
s"""
Ok rules
Name
$data
"""
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy