{"action":"created","comment":{"url":"https://api.github.com/repos/Normation/rudder/pulls/comments/32015643","id":32015643,"diff_hunk":"@@ -0,0 +1,248 @@\n+/*\n+*************************************************************************************\n+* Copyright 2015 Normation SAS\n+*************************************************************************************\n+*\n+* This program is free software: you can redistribute it and/or modify\n+* it under the terms of the GNU Affero General Public License as\n+* published by the Free Software Foundation, either version 3 of the\n+* License, or (at your option) any later version.\n+*\n+* In accordance with the terms of section 7 (7. Additional Terms.) of\n+* the GNU Affero GPL v3, the copyright holders add the following\n+* Additional permissions:\n+* Notwithstanding to the terms of section 5 (5. Conveying Modified Source\n+* Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU Affero GPL v3\n+* licence, when you create a Related Module, this Related Module is\n+* not considered as a part of the work and may be distributed under the\n+* license agreement of your choice.\n+* A \"Related Module\" means a set of sources files including their\n+* documentation that, without modification of the Source Code, enables\n+* supplementary functions or services in addition to those offered by\n+* the Software.\n+*\n+* This program is distributed in the hope that it will be useful,\n+* but WITHOUT ANY WARRANTY; without even the implied warranty of\n+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n+* GNU Affero General Public License for more details.\n+*\n+* You should have received a copy of the GNU Affero General Public License\n+* along with this program. If not, see <http://www.gnu.org/licenses/agpl.html>.\n+*\n+*************************************************************************************\n+*/\n+\n+package com.normation.rudder.web.rest.compliance\n+\n+import com.normation.inventory.domain.NodeId\n+import com.normation.rudder.domain.policies.DirectiveId\n+import com.normation.rudder.domain.policies.RuleId\n+import com.normation.rudder.domain.reports._\n+import net.liftweb.json.JsonDSL._\n+import com.normation.rudder.domain.policies.DirectiveId\n+\n+\n+\n+/**\n+ * Here, we want to present two view of compliance:\n+ * - by node\n+ * - by rule\n+ *\n+ * For \"by node\", we want to have a list of node, then rules,\n+ * then directives, then component and then values.\n+ *\n+ * For \"by rule\", we want to have a list of rules, then\n+ * directives, then components, THEN NODEs, then values.\n+ * Because we want to be able to analyse at the component level\n+ * if we have problems on several nodes, but values are only\n+ * meaningful in the context of a node.\n+ *\n+ * So we define two hierarchy of classes, with the name convention\n+ * starting by \"ByRule\" or \"ByNode\".\n+ */\n+\n+\n+/**\n+ * Compliance for a rules.\n+ * It lists:\n+ * - id: the rule id\n+ * - compliance: the compliance of the rule by node\n+ *   (total number of node, repartition of node by status)\n+ * - nodeCompliance: the list of compliance for each node, for that that rule\n+ */\n+case class ByRuleRuleCompliance(\n+    id             : RuleId\n+    //compliance by nodes\n+  , compliance     : ComplianceLevel\n+  , directives: Seq[ByRuleDirectiveCompliance]\n+)\n+\n+case class ByRuleDirectiveCompliance(\n+    id        : DirectiveId\n+  , compliance: ComplianceLevel\n+  , components: Seq[ByRuleComponentCompliance]\n+)\n+\n+case class ByRuleComponentCompliance(\n+    name      : String\n+  , compliance: ComplianceLevel\n+  , nodes     : Seq[ByRuleNodeCompliance]\n+)\n+\n+case class ByRuleNodeCompliance(\n+    id        : NodeId\n+  , values    : Seq[ComponentValueStatusReport]\n+)\n+\n+/**\n+ * Compliance for a node.\n+ * It lists:\n+ * - id: the node id\n+ * - compliance: the compliance of the node by rule\n+ *   (total number of rules, repartition of rules by status)\n+ * - ruleUnderNodeCompliance: the list of compliance for each rule, for that node\n+ */\n+case class ByNodeNodeCompliance(\n+    id             : NodeId\n+    //compliance by nodes\n+  , compliance     : ComplianceLevel\n+  , nodeCompliances: Seq[ByNodeRuleCompliance]\n+)\n+\n+/**\n+ * Compliance for rule, for a given set of directives\n+ * (normally, all the directive for a given rule)\n+ * It lists:\n+ * - id: the rule id\n+ * - compliance: total number of directives and\n+ *   repartition of directive compliance by status\n+ * - directiveCompliances: status for each directive, for that rule.\n+ */\n+case class ByNodeRuleCompliance(\n+    id        : RuleId\n+    //compliance by directive (by nodes)\n+  , compliance: ComplianceLevel\n+  , directives: Seq[ByNodeDirectiveCompliance]\n+)\n+\n+case class ByNodeDirectiveCompliance(\n+    id        : DirectiveId\n+  , compliance: ComplianceLevel\n+  , components: Map[String, ComponentStatusReport]\n+)\n+\n+object ByNodeDirectiveCompliance {\n+\n+  def apply(d: DirectiveStatusReport): ByNodeDirectiveCompliance = {\n+    new ByNodeDirectiveCompliance(d.directiveId, d.compliance, d.components)\n+  }\n+}\n+\n+object JsonCompliance {\n+\n+\n+  implicit class JsonbyRuleCompliance(rule: ByRuleRuleCompliance) {\n+    def toJson = (\n+        (\"id\" -> rule.id.value)\n+      ~ (\"compliance\" -> percents(rule.compliance))\n+      ~ (\"directives\" -> rule.directives.map { directive =>\n+          (\n+              (\"id\" -> directive.id.value)\n+            ~ (\"compliance\" -> percents(directive.compliance))\n+            ~ (\"components\" -> directive.components.map { component =>\n+                (\n+                    (\"name\" -> component.name)\n+                  ~ (\"compliance\" -> percents(component.compliance))\n+                  ~ (\"nodes\" -> component.nodes.map { node =>\n+                      (\n+                          (\"id\" -> node.id.value)\n+                        ~ (\"values\" -> node.values.map { value =>\n+                            (\n+                              (\"value\" -> value.componentValue)\n+                                ~ (\"reports\" -> value.messages.map { report =>\n+                                    (\n+                                        (\"status\" -> statusDisplayName(report.reportType))\n+                                      ~ (\"message\" -> report.message)\n+                                    )\n+                                })\n+                            )\n+                        })\n+                      )\n+                    })\n+                )\n+              })\n+          )\n+       })\n+   )\n+  }\n+\n+  implicit class JsonByNodeCompliance(n: ByNodeNodeCompliance) {\n+    def toJson = (\n+        (\"id\" -> n.id.value)\n+      ~ (\"compliance\" -> percents(n.compliance))\n+      ~ (\"rules\" -> n.nodeCompliances.map { rule =>\n+          (\n+              (\"id\" -> rule.id.value)\n+            ~ (\"compliance\" -> percents(rule.compliance))\n+            ~ (\"directives\" -> rule.directives.map { directive =>\n+                (\n+                    (\"id\" -> directive.id.value)\n+                  ~ (\"compliance\" -> percents(directive.compliance))\n+                  ~ (\"components\" -> directive.components.map { case (_, component) =>\n+                      (\n+                          (\"name\" -> component.componentName)\n+                        ~ (\"compliance\" -> percents(component.compliance))\n+                        ~ (\"values\" -> component.componentValues.map { case (_, value) =>\n+                            (\n+                              (\"value\" -> value.componentValue)\n+                                ~ (\"reports\" -> value.messages.map { report =>\n+                                    (\n+                                        (\"status\" -> statusDisplayName(report.reportType))\n+                                      ~ (\"message\" -> report.message)\n+                                    )\n+                                })\n+                            )\n+                        })\n+                      )\n+                    })\n+                )\n+             })\n+         )\n+        })\n+    )\n+  }\n+\n+  private[this] def statusDisplayName(r: ReportType): String = {","path":"rudder-web/src/main/scala/com/normation/rudder/web/rest/compliance/ComplianceAPIData.scala","position":215,"original_position":215,"commit_id":"85378945914e207ac558c6a63e1ee8e699678117","original_commit_id":"85378945914e207ac558c6a63e1ee8e699678117","user":{"login":"VinceMacBuche","id":1238978,"avatar_url":"https://avatars.githubusercontent.com/u/1238978?v=3","gravatar_id":"","url":"https://api.github.com/users/VinceMacBuche","html_url":"https://github.com/VinceMacBuche","followers_url":"https://api.github.com/users/VinceMacBuche/followers","following_url":"https://api.github.com/users/VinceMacBuche/following{/other_user}","gists_url":"https://api.github.com/users/VinceMacBuche/gists{/gist_id}","starred_url":"https://api.github.com/users/VinceMacBuche/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/VinceMacBuche/subscriptions","organizations_url":"https://api.github.com/users/VinceMacBuche/orgs","repos_url":"https://api.github.com/users/VinceMacBuche/repos","events_url":"https://api.github.com/users/VinceMacBuche/events{/privacy}","received_events_url":"https://api.github.com/users/VinceMacBuche/received_events","type":"User","site_admin":false},"body":"Understanding the need it fulfills, I agree with them!\r\n\r\nAnd i do not find a better way to name them and provide the same usefullness","created_at":"2015-06-09T14:04:09Z","updated_at":"2015-06-09T14:04:09Z","html_url":"https://github.com/Normation/rudder/pull/868#discussion_r32015643","pull_request_url":"https://api.github.com/repos/Normation/rudder/pulls/868","_links":{"self":{"href":"https://api.github.com/repos/Normation/rudder/pulls/comments/32015643"},"html":{"href":"https://github.com/Normation/rudder/pull/868#discussion_r32015643"},"pull_request":{"href":"https://api.github.com/repos/Normation/rudder/pulls/868"}}},"pull_request":{"url":"https://api.github.com/repos/Normation/rudder/pulls/868","id":36399423,"html_url":"https://github.com/Normation/rudder/pull/868","diff_url":"https://github.com/Normation/rudder/pull/868.diff","patch_url":"https://github.com/Normation/rudder/pull/868.patch","issue_url":"https://api.github.com/repos/Normation/rudder/issues/868","number":868,"state":"open","locked":false,"title":"Issue #6137: add an API for compliance by node or rule","user":{"login":"fanf","id":44649,"avatar_url":"https://avatars.githubusercontent.com/u/44649?v=3","gravatar_id":"","url":"https://api.github.com/users/fanf","html_url":"https://github.com/fanf","followers_url":"https://api.github.com/users/fanf/followers","following_url":"https://api.github.com/users/fanf/following{/other_user}","gists_url":"https://api.github.com/users/fanf/gists{/gist_id}","starred_url":"https://api.github.com/users/fanf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fanf/subscriptions","organizations_url":"https://api.github.com/users/fanf/orgs","repos_url":"https://api.github.com/users/fanf/repos","events_url":"https://api.github.com/users/fanf/events{/privacy}","received_events_url":"https://api.github.com/users/fanf/received_events","type":"User","site_admin":false},"body":"See http://www.rudder-project.org/redmine/issues/6137 for details about what is added","created_at":"2015-05-28T12:06:24Z","updated_at":"2015-06-09T14:04:09Z","closed_at":null,"merged_at":null,"merge_commit_sha":"0b5d65270ac7bee324ff81a06f499e4516d03503","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/Normation/rudder/pulls/868/commits","review_comments_url":"https://api.github.com/repos/Normation/rudder/pulls/868/comments","review_comment_url":"https://api.github.com/repos/Normation/rudder/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Normation/rudder/issues/868/comments","statuses_url":"https://api.github.com/repos/Normation/rudder/statuses/85378945914e207ac558c6a63e1ee8e699678117","head":{"label":"fanf:ust_6137/create_compliance_api","ref":"ust_6137/create_compliance_api","sha":"85378945914e207ac558c6a63e1ee8e699678117","user":{"login":"fanf","id":44649,"avatar_url":"https://avatars.githubusercontent.com/u/44649?v=3","gravatar_id":"","url":"https://api.github.com/users/fanf","html_url":"https://github.com/fanf","followers_url":"https://api.github.com/users/fanf/followers","following_url":"https://api.github.com/users/fanf/following{/other_user}","gists_url":"https://api.github.com/users/fanf/gists{/gist_id}","starred_url":"https://api.github.com/users/fanf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fanf/subscriptions","organizations_url":"https://api.github.com/users/fanf/orgs","repos_url":"https://api.github.com/users/fanf/repos","events_url":"https://api.github.com/users/fanf/events{/privacy}","received_events_url":"https://api.github.com/users/fanf/received_events","type":"User","site_admin":false},"repo":{"id":6705258,"name":"rudder","full_name":"fanf/rudder","owner":{"login":"fanf","id":44649,"avatar_url":"https://avatars.githubusercontent.com/u/44649?v=3","gravatar_id":"","url":"https://api.github.com/users/fanf","html_url":"https://github.com/fanf","followers_url":"https://api.github.com/users/fanf/followers","following_url":"https://api.github.com/users/fanf/following{/other_user}","gists_url":"https://api.github.com/users/fanf/gists{/gist_id}","starred_url":"https://api.github.com/users/fanf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fanf/subscriptions","organizations_url":"https://api.github.com/users/fanf/orgs","repos_url":"https://api.github.com/users/fanf/repos","events_url":"https://api.github.com/users/fanf/events{/privacy}","received_events_url":"https://api.github.com/users/fanf/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/fanf/rudder","description":"","fork":true,"url":"https://api.github.com/repos/fanf/rudder","forks_url":"https://api.github.com/repos/fanf/rudder/forks","keys_url":"https://api.github.com/repos/fanf/rudder/keys{/key_id}","collaborators_url":"https://api.github.com/repos/fanf/rudder/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/fanf/rudder/teams","hooks_url":"https://api.github.com/repos/fanf/rudder/hooks","issue_events_url":"https://api.github.com/repos/fanf/rudder/issues/events{/number}","events_url":"https://api.github.com/repos/fanf/rudder/events","assignees_url":"https://api.github.com/repos/fanf/rudder/assignees{/user}","branches_url":"https://api.github.com/repos/fanf/rudder/branches{/branch}","tags_url":"https://api.github.com/repos/fanf/rudder/tags","blobs_url":"https://api.github.com/repos/fanf/rudder/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/fanf/rudder/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/fanf/rudder/git/refs{/sha}","trees_url":"https://api.github.com/repos/fanf/rudder/git/trees{/sha}","statuses_url":"https://api.github.com/repos/fanf/rudder/statuses/{sha}","languages_url":"https://api.github.com/repos/fanf/rudder/languages","stargazers_url":"https://api.github.com/repos/fanf/rudder/stargazers","contributors_url":"https://api.github.com/repos/fanf/rudder/contributors","subscribers_url":"https://api.github.com/repos/fanf/rudder/subscribers","subscription_url":"https://api.github.com/repos/fanf/rudder/subscription","commits_url":"https://api.github.com/repos/fanf/rudder/commits{/sha}","git_commits_url":"https://api.github.com/repos/fanf/rudder/git/commits{/sha}","comments_url":"https://api.github.com/repos/fanf/rudder/comments{/number}","issue_comment_url":"https://api.github.com/repos/fanf/rudder/issues/comments{/number}","contents_url":"https://api.github.com/repos/fanf/rudder/contents/{+path}","compare_url":"https://api.github.com/repos/fanf/rudder/compare/{base}...{head}","merges_url":"https://api.github.com/repos/fanf/rudder/merges","archive_url":"https://api.github.com/repos/fanf/rudder/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/fanf/rudder/downloads","issues_url":"https://api.github.com/repos/fanf/rudder/issues{/number}","pulls_url":"https://api.github.com/repos/fanf/rudder/pulls{/number}","milestones_url":"https://api.github.com/repos/fanf/rudder/milestones{/number}","notifications_url":"https://api.github.com/repos/fanf/rudder/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/fanf/rudder/labels{/name}","releases_url":"https://api.github.com/repos/fanf/rudder/releases{/id}","created_at":"2012-11-15T13:39:53Z","updated_at":"2014-06-06T15:06:41Z","pushed_at":"2015-05-29T07:39:34Z","git_url":"git://github.com/fanf/rudder.git","ssh_url":"git@github.com:fanf/rudder.git","clone_url":"https://github.com/fanf/rudder.git","svn_url":"https://github.com/fanf/rudder","homepage":"http://www.rudder-project.org/","size":19678,"stargazers_count":0,"watchers_count":0,"language":"Scala","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"Normation:branches/rudder/3.1","ref":"branches/rudder/3.1","sha":"2042abeb31b262c599de5d4faf39bbb454b7ccb3","user":{"login":"Normation","id":695105,"avatar_url":"https://avatars.githubusercontent.com/u/695105?v=3","gravatar_id":"","url":"https://api.github.com/users/Normation","html_url":"https://github.com/Normation","followers_url":"https://api.github.com/users/Normation/followers","following_url":"https://api.github.com/users/Normation/following{/other_user}","gists_url":"https://api.github.com/users/Normation/gists{/gist_id}","starred_url":"https://api.github.com/users/Normation/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Normation/subscriptions","organizations_url":"https://api.github.com/users/Normation/orgs","repos_url":"https://api.github.com/users/Normation/repos","events_url":"https://api.github.com/users/Normation/events{/privacy}","received_events_url":"https://api.github.com/users/Normation/received_events","type":"Organization","site_admin":false},"repo":{"id":2528254,"name":"rudder","full_name":"Normation/rudder","owner":{"login":"Normation","id":695105,"avatar_url":"https://avatars.githubusercontent.com/u/695105?v=3","gravatar_id":"","url":"https://api.github.com/users/Normation","html_url":"https://github.com/Normation","followers_url":"https://api.github.com/users/Normation/followers","following_url":"https://api.github.com/users/Normation/following{/other_user}","gists_url":"https://api.github.com/users/Normation/gists{/gist_id}","starred_url":"https://api.github.com/users/Normation/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Normation/subscriptions","organizations_url":"https://api.github.com/users/Normation/orgs","repos_url":"https://api.github.com/users/Normation/repos","events_url":"https://api.github.com/users/Normation/events{/privacy}","received_events_url":"https://api.github.com/users/Normation/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Normation/rudder","description":"","fork":false,"url":"https://api.github.com/repos/Normation/rudder","forks_url":"https://api.github.com/repos/Normation/rudder/forks","keys_url":"https://api.github.com/repos/Normation/rudder/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Normation/rudder/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Normation/rudder/teams","hooks_url":"https://api.github.com/repos/Normation/rudder/hooks","issue_events_url":"https://api.github.com/repos/Normation/rudder/issues/events{/number}","events_url":"https://api.github.com/repos/Normation/rudder/events","assignees_url":"https://api.github.com/repos/Normation/rudder/assignees{/user}","branches_url":"https://api.github.com/repos/Normation/rudder/branches{/branch}","tags_url":"https://api.github.com/repos/Normation/rudder/tags","blobs_url":"https://api.github.com/repos/Normation/rudder/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Normation/rudder/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Normation/rudder/git/refs{/sha}","trees_url":"https://api.github.com/repos/Normation/rudder/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Normation/rudder/statuses/{sha}","languages_url":"https://api.github.com/repos/Normation/rudder/languages","stargazers_url":"https://api.github.com/repos/Normation/rudder/stargazers","contributors_url":"https://api.github.com/repos/Normation/rudder/contributors","subscribers_url":"https://api.github.com/repos/Normation/rudder/subscribers","subscription_url":"https://api.github.com/repos/Normation/rudder/subscription","commits_url":"https://api.github.com/repos/Normation/rudder/commits{/sha}","git_commits_url":"https://api.github.com/repos/Normation/rudder/git/commits{/sha}","comments_url":"https://api.github.com/repos/Normation/rudder/comments{/number}","issue_comment_url":"https://api.github.com/repos/Normation/rudder/issues/comments{/number}","contents_url":"https://api.github.com/repos/Normation/rudder/contents/{+path}","compare_url":"https://api.github.com/repos/Normation/rudder/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Normation/rudder/merges","archive_url":"https://api.github.com/repos/Normation/rudder/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Normation/rudder/downloads","issues_url":"https://api.github.com/repos/Normation/rudder/issues{/number}","pulls_url":"https://api.github.com/repos/Normation/rudder/pulls{/number}","milestones_url":"https://api.github.com/repos/Normation/rudder/milestones{/number}","notifications_url":"https://api.github.com/repos/Normation/rudder/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Normation/rudder/labels{/name}","releases_url":"https://api.github.com/repos/Normation/rudder/releases{/id}","created_at":"2011-10-06T19:27:50Z","updated_at":"2015-06-02T21:37:47Z","pushed_at":"2015-06-08T21:24:23Z","git_url":"git://github.com/Normation/rudder.git","ssh_url":"git@github.com:Normation/rudder.git","clone_url":"https://github.com/Normation/rudder.git","svn_url":"https://github.com/Normation/rudder","homepage":"http://www.rudder-project.org/","size":22515,"stargazers_count":42,"watchers_count":42,"language":"Scala","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":14,"mirror_url":null,"open_issues_count":3,"forks":14,"open_issues":3,"watchers":42,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/Normation/rudder/pulls/868"},"html":{"href":"https://github.com/Normation/rudder/pull/868"},"issue":{"href":"https://api.github.com/repos/Normation/rudder/issues/868"},"comments":{"href":"https://api.github.com/repos/Normation/rudder/issues/868/comments"},"review_comments":{"href":"https://api.github.com/repos/Normation/rudder/pulls/868/comments"},"review_comment":{"href":"https://api.github.com/repos/Normation/rudder/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Normation/rudder/pulls/868/commits"},"statuses":{"href":"https://api.github.com/repos/Normation/rudder/statuses/85378945914e207ac558c6a63e1ee8e699678117"}}}}