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 #24779: Groups compliance summary need API pagination #5635

Draft
wants to merge 1 commit into
base: branches/rudder/8.1
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

package com.normation.rudder.apidata

import cats.syntax.traverse.*
import com.normation.GitVersion
import com.normation.GitVersion.ParseRev
import com.normation.GitVersion.Revision
Expand All @@ -59,6 +60,7 @@ import com.normation.rudder.domain.properties.PropertyProvider
import com.normation.rudder.domain.queries.NodeReturnType
import com.normation.rudder.domain.queries.Query
import com.normation.rudder.domain.queries.QueryReturnType
import com.normation.rudder.domain.reports.CompliancePrecision
import com.normation.rudder.rule.category.RuleCategory
import com.normation.rudder.rule.category.RuleCategoryId
import com.normation.rudder.services.queries.CmdbQueryParser
Expand Down Expand Up @@ -622,4 +624,14 @@ class ZioJsonExtractor(queryParser: CmdbQueryParser with JsonQueryLexer) {
}
}

def extractCompliancePrecisionFromParams(params: Map[String, List[String]]): PureResult[Option[CompliancePrecision]] = {
for {
precision <-
params.parseString("precision", s => s.toIntOption.toRight(s"percent precison must be an integer, was: '${s}'"))
res <- precision.traverse(CompliancePrecision.fromPrecision(_).toPureResult)
} yield {
res
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -244,11 +244,6 @@ trait RoNodeGroupRepository {
// TODO: add QC
def getAll(): IOResult[Seq[NodeGroup]]

/**
* Get all node groups by ids
*/
def getAllByIds(ids: Seq[NodeGroupId]): IOResult[Seq[NodeGroup]]

/**
* Get all the node group id and the set of ndoes within
* Goal is to be more efficient
Expand Down Expand Up @@ -277,6 +272,14 @@ trait RoNodeGroupRepository {
qc: QueryContext
): IOResult[SortedMap[List[NodeGroupCategoryId], CategoryAndNodeGroup]]

/**
* Get all node groups grouped by their direct parent category.
* Group ids can be filtered, by default return all groups with an empty filter.
*/
def getGroupsByCategoryByIds(ids: Seq[NodeGroupId] = Seq.empty, includeSystem: Boolean = false)(implicit
qc: QueryContext
): IOResult[Map[NodeGroupCategory, Seq[NodeGroup]]]

/**
* Retrieve all groups that have at least one of the given
* node ID in there member list.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,52 @@ class RoLDAPNodeGroupRepository(
}
}

def getGroupsByCategoryByIds(ids: Seq[NodeGroupId], includeSystem: Boolean = false)(implicit
qc: QueryContext
): IOResult[Map[NodeGroupCategory, Seq[NodeGroup]]] = {
groupLibMutex.readLock {
for {
con <- ldap
entries <- if (ids.isEmpty) con.searchSub(rudderDit.GROUP.dn, IS(OC_RUDDER_NODE_GROUP))
else con.searchSub(rudderDit.GROUP.dn, OR(ids.map(id => EQ(A_NODE_GROUP_UUID, id.serialize))*))
groups <- ZIO.foreach(entries)(groupEntry => {
for {
g <- mapper
.entry2NodeGroup(groupEntry)
.toIO
.chainError(s"Error when mapping server group entry into a Group instance. Entry: ${groupEntry}")
allNodes <- nodeFactRepo.getAll()
nodeIds = g.serverList.intersect(allNodes.keySet.toSet)
y = g.copy(serverList = nodeIds)
} yield (groupEntry, y)
})
cats <-
ZIO.foreach(groups) {
case (groupEntry, g) => {
for {
parentCategoryEntry <-
con
.get(groupEntry.dn.getParent)
.notOptional(s"Parent category of entry with ID '${g.id.serialize}' was not found")
parentCategory <-
mapper
.entry2NodeGroupCategory(parentCategoryEntry)
.toIO
.chainError(
"Error when transforming LDAP entry %s into an active technique category".format(parentCategoryEntry)
)
} yield {
parentCategory
}
}
}
result = cats.zip(groups).groupBy(_._1).map { case (cat, pairs) => (cat, pairs.map(_._2._2)) }
} yield {
result
}
}
}

def getNodeGroupOpt(id: NodeGroupId)(implicit qc: QueryContext): IOResult[Option[(NodeGroup, NodeGroupCategoryId)]] = {
groupLibMutex.readLock(for {
con <- ldap
Expand Down Expand Up @@ -444,23 +490,6 @@ class RoLDAPNodeGroupRepository(
}
}

def getAllByIds(ids: Seq[NodeGroupId]): IOResult[Seq[NodeGroup]] = {
for {
con <- ldap
// for each directive entry, map it. if one fails, all fails
entries <-
groupLibMutex.readLock(con.searchSub(rudderDit.GROUP.dn, OR(ids.map(id => EQ(A_NODE_GROUP_UUID, id.serialize))*)))
groups <- ZIO.foreach(entries)(groupEntry => {
mapper
.entry2NodeGroup(groupEntry)
.toIO
.chainError(s"Error when transforming LDAP entry into a Group instance. Entry: ${groupEntry}")
})
} yield {
groups
}
}

def getAllNodeIds(): IOResult[Map[NodeGroupId, Set[NodeId]]] = {
for {
con <- ldap
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2381,8 +2381,17 @@ class MockNodeGroups(nodesRepo: MockNodes) {
} yield cat
}
override def getAll(): IOResult[Seq[NodeGroup]] = categories.get.map(_.allGroups.values.map(_.nodeGroup).toSeq)
override def getAllByIds(ids: Seq[NodeGroupId]): IOResult[Seq[NodeGroup]] = {
categories.get.map(_.allGroups.values.map(_.nodeGroup).filter(g => ids.contains(g.id)).toSeq)
override def getGroupsByCategoryByIds(ids: Seq[NodeGroupId], includeSystem: Boolean = false)(implicit
qc: QueryContext
): IOResult[Map[NodeGroupCategory, Seq[NodeGroup]]] = {
categories.get.map { root =>
val groups = root.allGroups.values.map(_.nodeGroup).filter(g => ids.contains(g.id)).toSeq
val categories = groups.map(g => root.categoryByGroupId(g.id)).distinct
categories.map { c =>
val cat = root.allCategories(c).toNodeGroupCategory
(cat, groups.filter(g => root.categoryByGroupId(g.id) == c))
}.toMap
}
}

override def getAllNodeIds(): IOResult[Map[NodeGroupId, Set[NodeId]]] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,16 @@ import com.normation.rudder.domain.reports.ComplianceLevel
import com.normation.rudder.reports.ComplianceModeName
import com.normation.rudder.repository.FullActiveTechnique
import enumeratum.*
import io.scalaland.chimney.Transformer
import java.lang
import net.liftweb.json.*
import net.liftweb.json.JsonAST
import net.liftweb.json.JsonDSL.*
import org.apache.commons.csv.CSVFormat
import org.apache.commons.csv.QuoteMode
import scala.collection.immutable
import zio.json.DeriveJsonEncoder
import zio.json.JsonEncoder

/**
* Here, we want to present two views of compliance:
Expand Down Expand Up @@ -129,6 +132,35 @@ final case class ByDirectiveByNodeRuleCompliance(
components: Seq[ByRuleByNodeByDirectiveByComponentCompliance]
)

final case class ByNodeGroupFullCompliance(
id: String,
name: String,
category: String,
global: GenericCompliance,
targeted: GenericCompliance
)

final case class GenericCompliance(
id: String,
name: String,
compliance: ComplianceLevel,
mode: ComplianceModeName,
complianceDetails: ComplianceSerializable
)
object GenericCompliance {
implicit def transformByNodeGroupCompliance(implicit
precision: CompliancePrecision
): Transformer[ByNodeGroupCompliance, GenericCompliance] = {
Transformer
.define[ByNodeGroupCompliance, GenericCompliance]
.withFieldComputed(
_.complianceDetails,
b => ComplianceSerializable.fromPercent(CompliancePercent.fromLevels(b.compliance, precision))
)
.buildTransformer
}
}

final case class ByNodeGroupCompliance(
id: String,
name: String,
Expand Down Expand Up @@ -511,6 +543,18 @@ object CsvCompliance {
}

object JsonCompliance {
implicit val complianceModeNameEncoder: JsonEncoder[ComplianceModeName] = JsonEncoder[String].contramap(_.name)
implicit val complianceSerializableEncoder: JsonEncoder[ComplianceSerializable] = DeriveJsonEncoder.gen[ComplianceSerializable]

class ComplianceEncoders(implicit val precision: CompliancePrecision) {
implicit val complianceLevelEncoder: JsonEncoder[ComplianceLevel] = {
JsonEncoder[Double].contramap(_.complianceWithoutPending(precision))
}
implicit val genericComplianceEncoder: JsonEncoder[GenericCompliance] =
DeriveJsonEncoder.gen[GenericCompliance]
implicit val byNodeGroupFullComplianceEncoder: JsonEncoder[ByNodeGroupFullCompliance] =
DeriveJsonEncoder.gen[ByNodeGroupFullCompliance]
}

// global compliance
implicit class JsonGlobalCompliance(val optCompliance: Option[(ComplianceLevel, Long)]) extends AnyVal {
Expand Down
Loading