Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #24637: Improvements on score: F score, rework tooltip, improve messages #5558

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ CREATE TABLE NodeFacts (
);


CREATE TYPE score AS enum ('A', 'B', 'C', 'D', 'E');
CREATE TYPE score AS enum ('A', 'B', 'C', 'D', 'E', 'F', 'X');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This leads to SQL errors for people with the beta2 since there is no migration. Pondering if it's important, not sure, but we should at least add in release notes:
"if you upgrade from a beta version, you need to do:

rudder=> ALTER TYPE score ADD VALUE 'F';
ALTER TYPE
rudder=> ALTER TYPE score ADD VALUE 'X';
ALTER TYPE
rudder=>

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't support update between beta, we never did nor we want to support it


Create table GlobalScore (
nodeId text primary key
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import com.normation.rudder.score.ScoreValue.B
import com.normation.rudder.score.ScoreValue.C
import com.normation.rudder.score.ScoreValue.D
import com.normation.rudder.score.ScoreValue.E
import com.normation.rudder.score.ScoreValue.F
import zio.*
import zio.json.*
import zio.syntax.*
Expand All @@ -64,17 +65,19 @@ object ComplianceScoreEventHandler extends ScoreEventHandler {
p <- ComplianceSerializable.fromPercent(percent).toJsonAST
} yield {
import ComplianceScore.scoreId
val score = if (percent.compliance >= 100) {
Score(scoreId, A, "Node is compliant at 100%", p)
} else if (percent.compliance >= 75) {
Score(scoreId, B, "Node is compliant at least at 75%", p)
val score = if (percent.compliance >= 95) {
Score(scoreId, A, "Compliance is over 95%", p)
} else if (percent.compliance >= 80) {
Score(scoreId, B, "Compliance is between 80% and 95%", p)
} else if (percent.compliance >= 50) {
ComplianceSerializable.fromPercent(percent)
Score(scoreId, C, "Node is compliant at least at 50%", p)
Score(scoreId, C, "Compliance is between 50% and 80%", p)
} else if (percent.compliance >= 20) {
Score(scoreId, D, "Compliance is between 20% and 50%", p)
} else if (percent.compliance >= 25) {
Score(scoreId, D, "Node is compliant at least at 25%", p)
Score(scoreId, E, "Compliance is between 5% and 20%", p)
} else {
Score(scoreId, E, "Node is compliant at less than 25%", p)
Score(scoreId, F, "Compliance is lower then 5%", p)
fanf marked this conversation as resolved.
Show resolved Hide resolved
}
((n, score :: Nil) :: Nil)
}) match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ object ScoreValue extends Enum[ScoreValue] {
case object C extends ScoreValue { val value = "C" }
case object D extends ScoreValue { val value = "D" }
case object E extends ScoreValue { val value = "E" }
case object F extends ScoreValue { val value = "F" }
case object NoScore extends ScoreValue { val value = "X" }

val values: IndexedSeq[ScoreValue] = findValues
Expand All @@ -80,14 +81,18 @@ object GlobalScoreService {
NoDetailsScore(newScore.scoreId, newScore.value, newScore.message) :: acc.filterNot(_.scoreId == newScore.scoreId)
}
import ScoreValue.*
val score = if (correctScores.exists(_.value == E)) { E }
val score = if (correctScores.exists(_.value == F)) { F }
else if (correctScores.exists(_.value == E)) { E }
else if (correctScores.exists(_.value == D)) { D }
else if (correctScores.exists(_.value == C)) {
C
} else if (correctScores.exists(_.value == B)) {
B
} else A
GlobalScore(score, s"There is at least a Score with ${score.value}", correctScores)
else if (correctScores.exists(_.value == C)) { C }
else if (correctScores.exists(_.value == B)) { B }
else A

if (correctScores.isEmpty) {
GlobalScore(NoScore, "No Score defined for this node", correctScores)
} else {
GlobalScore(score, s"Worst score for this node is ${score.value}", correctScores)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
*
* You should have received a copy of the GNU General Public License
* along with Rudder. If not, see <http://www.gnu.org/licenses/>.

*
*
*************************************************************************************
*/
Expand All @@ -50,6 +50,7 @@ import com.normation.rudder.score.ScoreValue.B
import com.normation.rudder.score.ScoreValue.C
import com.normation.rudder.score.ScoreValue.D
import com.normation.rudder.score.ScoreValue.E
import com.normation.rudder.score.ScoreValue.F
import zio.*
import zio.json.*
import zio.syntax.*
Expand Down Expand Up @@ -97,15 +98,29 @@ class SystemUpdateScoreHandler(nodeFactRepository: NodeFactRepository) extends S
} yield {
import SystemUpdateScore.scoreId
val score = if (security == 0 && sum < 50) {
Score(scoreId, A, "Node has no security updates and less than 50 updates available", stats)
} else if (security < 5) {
Score(scoreId, B, s"Node has ${security} security updates available (less than 5)", stats)
} else if (security < 20) {
Score(scoreId, C, s"Node has ${security} security updates available (less than 20)", stats)
} else if (security < 50) {
Score(scoreId, D, s"Node has ${security} security updates available (less than 50)", stats)
val securityMessage = "No security update."
val updateMessage = s"${sum} updates available (less than 50)."
Score(scoreId, A, s"${securityMessage}\n${updateMessage}", stats)
} else if (security < 5 && sum < 75) {
val securityMessage = if (security < 5) s"${security} security updates available (less than 5)." else ""
val updateMessage = if (sum < 75) s"${sum} updates available (between 50 and 75)." else ""
Score(scoreId, B, s"${securityMessage}\n${updateMessage}", stats)
} else if (security < 20 && sum < 125) {
val securityMessage = if (security < 20) s"${security} security updates available (between 5 and 20)." else ""
val updateMessage = if (sum < 125) s"${sum} updates available (between 75 and 125)." else ""
Score(scoreId, C, s"${securityMessage}\n${updateMessage}", stats)
} else if (security < 50 && sum < 175) {
val securityMessage = if (security < 50) s"${security} security updates available (between 20 and 50)." else ""
val updateMessage = if (sum < 175) s"${sum} updates available (between 125 and 175)." else ""
Score(scoreId, D, s"${securityMessage}\n${updateMessage}", stats)
} else if (security < 80 && sum < 250) {
val securityMessage = if (security < 80) s"${security} security updates available (between 50 and 80)." else ""
val updateMessage = if (sum < 250) s"${sum} updates available (between 175 and 250)." else ""
Score(scoreId, E, s"${securityMessage}\n${updateMessage}", stats)
} else {
Score(scoreId, E, s"Node has ${security} security updates available (more than 50)", stats)
val securityMessage = if (security >= 80) s"${security} security updates available (more than 80)." else ""
val updateMessage = if (sum >= 250) s"${sum} updates available (More than 250)." else ""
Score(scoreId, F, s"${securityMessage}\n${updateMessage}", stats)
}
((n, score :: Nil) :: Nil)
}) match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ object AuthorizationApiMapping {
NodeApi.ListAcceptedNodes.x :: NodeApi.ListPendingNodes.x :: NodeApi.NodeDetails.x ::
NodeApi.NodeInheritedProperties.x :: NodeApi.NodeDisplayInheritedProperties.x :: NodeApi.NodeDetailsTable.x ::
NodeApi.PendingNodeDetails.x :: NodeApi.NodeDetailsSoftware.x :: NodeApi.NodeDetailsProperty.x ::
NodeApi.GetNodesStatus.x :: InventoryApi.QueueInformation.x ::
NodeApi.NodeGlobalScore.x :: NodeApi.NodeScoreDetail.x :: NodeApi.NodeScoreDetails.x ::
NodeApi.GetNodesStatus.x ::
// score about node
NodeApi.NodeGlobalScore.x :: NodeApi.NodeScoreDetails.x :: NodeApi.NodeScoreDetail.x ::
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
*************************************************************************************
* Copyright 2017 Normation SAS
*************************************************************************************

*
* This file is part of Rudder.
*
* Rudder is free software: you can redistribute it and/or modify
Expand Down Expand Up @@ -30,7 +30,7 @@
*
* You should have received a copy of the GNU General Public License
* along with Rudder. If not, see <http://www.gnu.org/licenses/>.

*
*
*************************************************************************************
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ buildTooltipContent : String -> String -> String
buildTooltipContent title content =
let
headingTag = "<h4 class='tags-tooltip-title'>"
contentTag = "</h4><div class='tooltip-inner-content'>"
closeTag = "</div>"
contentTag = "</h4><div class='tooltip-inner-content'><pre>"
closeTag = "</pre></div>"
in
headingTag ++ title ++ contentTag ++ content ++ closeTag
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ update msg model =
)
Err err ->
processApiError "Getting global score" err model
ShowScoreMessage optScore ->
( {model | scoreToShow = optScore} , Cmd.none )
GetScoreInfo res ->
case res of
Ok scoreInfo ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type alias Model =
, score : Maybe GlobalScore
, contextPath : String
, scoreInfo : List ScoreInfo
, scoreToShow : Maybe String
}

type Msg = GetScore (Result Error GlobalScore) | GetScoreInfo (Result Error (List ScoreInfo))
type Msg = GetScore (Result Error GlobalScore) | GetScoreInfo (Result Error (List ScoreInfo)) | ShowScoreMessage (Maybe String)
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ subscriptions _ = Sub.none
init : { id : String, contextPath : String } -> ( Model, Cmd Msg )
init flags =
let
initModel = Model (NodeId flags.id) Nothing flags.contextPath []
initModel = Model (NodeId flags.id) Nothing flags.contextPath [] Nothing

action = Cmd.batch [ getScore initModel, getScoreInfo initModel ]
in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,47 @@ module Score.View exposing (..)

import Html exposing (..)
import Html.Attributes exposing (..)
import List.Extra
import Markdown

import Score.DataTypes exposing (..)
import Score.ViewUtils exposing (..)
import String.Extra

view : Model -> Html Msg
view model =
div[ class "compliance-score d-flex flex-column mb-4" ]
[ div[class "global-score d-flex"]
( case model.score of
Just complianceScore ->
let

(message,name) = case model.scoreToShow of
Nothing -> (complianceScore.message, "Global")
Just scoreId ->
let
msg = complianceScore.details |> List.Extra.find (.scoreId >> (==) scoreId) |> Maybe.map .message |> Maybe.withDefault complianceScore.message
n = List.Extra.find (.id >> (==) scoreId) model.scoreInfo |> Maybe.map .name |> Maybe.withDefault (String.Extra.humanize scoreId)
in
(msg,n)
in
[ div[class "score-badge"]
[ getScoreBadge complianceScore.value [] False
[ getScoreBadge Nothing complianceScore.value False
]
, div[class "score-breakdown ps-5 flex-grow-1 flex-column"]
[ h3[][text "Score breakdown"]
, div[class "d-flex"](scoreBreakdownList complianceScore.details model.scoreInfo)
]
, div[class "score-explanation ps-3 flex-grow-1"]
( Markdown.toHtml Nothing complianceScore.message )
(( h3 [] [text name ] ) ::
( Markdown.toHtml Nothing message ))
]
Nothing ->
let
noComplianceMsg = "There is no score for this node"
in
[ div[class "score-badge sm "]
[ getScoreBadge X [] False
[ getScoreBadge Nothing X False
]
, div[class "no-compliance d-flex flex-grow-1 align-items-center ps-4"]
[ i[class "fa fa-warning"][]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module Score.ViewUtils exposing (..)

import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onMouseLeave, onMouseOver)
import List
import List.Extra
import String.Extra
Expand All @@ -20,20 +21,13 @@ scoreLabel score =
F -> "F"
X -> "X"

buildTooltipBadge : String -> String -> List (Attribute msg)
buildTooltipBadge name msg =
[ attribute "data-bs-toggle" "tooltip"
, attribute "data-bs-placement" "top"
, title (buildTooltipContent (String.Extra.humanize name) msg)
]

getScoreBadge : ScoreValue -> List (Attribute Msg) -> Bool -> Html Msg
getScoreBadge score tooltipAttributes smallSize =

getScoreBadge : Maybe String -> ScoreValue -> Bool -> Html Msg
getScoreBadge id score smallSize =
span
( List.append
[ class ("badge-compliance-score " ++ (scoreLabel score) ++ (if smallSize then " sm" else ""))]
tooltipAttributes
)[]
[ onMouseOver (ShowScoreMessage id), onMouseLeave (ShowScoreMessage Nothing), class ("badge-compliance-score " ++ (scoreLabel score) ++ (if smallSize then " sm" else ""))]
[]

scoreBreakdownList : List Score -> List ScoreInfo -> List (Html Msg)
scoreBreakdownList scoreDetails scoreInfo = scoreDetails
Expand All @@ -42,7 +36,7 @@ scoreBreakdownList scoreDetails scoreInfo = scoreDetails
name = List.Extra.find (.id >> (==) sD.scoreId) scoreInfo |> Maybe.map .name |> Maybe.withDefault (String.Extra.humanize sD.scoreId)
in
div[class "d-flex flex-column pe-5 align-items-center"]
[ getScoreBadge sD.value (buildTooltipBadge sD.scoreId sD.message) True
[ getScoreBadge (Just sD.scoreId) sD.value True
, label[class "text-center pt-2"][text name ]
]
)
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class CheckTableScore(
def createScoreTables: IOResult[Unit] = {

val checkType = sql"SELECT 1 FROM pg_type WHERE typname = 'score'"
val sqlType = sql"""CREATE TYPE score AS enum ('A', 'B', 'C', 'D', 'E')"""
val sqlType = sql"""CREATE TYPE score AS enum ('A', 'B', 'C', 'D', 'E', 'F', 'X')"""
val sql1 = sql"""CREATE TABLE IF NOT EXISTS GlobalScore (
nodeId text primary key
, score score NOT NULL
Expand Down