Skip to content

Commit f6d1f3c

Browse files
committed
Export rule severities and impact separately
1 parent 709152f commit f6d1f3c

File tree

2 files changed

+64
-16
lines changed

2 files changed

+64
-16
lines changed

sonar/qualityprofiles.py

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -393,11 +393,15 @@ def to_json(self, export_settings: types.ConfigSettings) -> types.ObjectJsonRepr
393393
json_data.pop("isBuiltIn", None)
394394
json_data["rules"] = []
395395
for rule in self.rules().values():
396-
data = {k: v for k, v in rule.export(full).items() if k not in ("isTemplate", "templateKey", "language", "tags", "severities")}
396+
data = {
397+
k: v for k, v in rule.export(full).items() if k not in ("isTemplate", "templateKey", "language", "tags", "severities", "impacts")
398+
}
397399
if self.rule_is_prioritized(rule.key):
398400
data["prioritized"] = True
399-
if self.rule_has_custom_severities(rule.key):
400-
data["severities"] = self.rule_impacts(rule.key, substitute_with_default=True)
401+
if self.rule_has_custom_severity(rule.key):
402+
data["severity"] = self.rule_severity(rule.key, substitute_with_default=True)
403+
if self.rule_has_custom_impacts(rule.key):
404+
data["impacts"] = {k: v for k, v in self.rule_impacts(rule.key, substitute_with_default=True).items() if v != c.DEFAULT}
401405
json_data["rules"].append({"key": rule.key, **data})
402406
json_data["permissions"] = self.permissions().export(export_settings)
403407
return util.remove_nones(util.filter_export(json_data, _IMPORTABLE_PROPERTIES, full))
@@ -422,21 +426,32 @@ def api_params(self, op: str = c.GET) -> types.ApiParams:
422426
return operations[op] if op in operations else operations[c.GET]
423427

424428
def rule_impacts(self, rule_key: str, substitute_with_default: bool = True) -> dict[str, str]:
425-
"""Returns the severities of a rule in the quality profile
429+
"""Returns the impacts of a rule in the quality profile
426430
427431
:param str rule_key: The rule key to get severities for
428-
:return: The severities of the rule in the quality profile
432+
:return: The impacts of the rule in the quality profile
429433
:rtype: dict[str, str]
430434
"""
431435
return rules.Rule.get_object(self.endpoint, rule_key).impacts(self.key, substitute_with_default=substitute_with_default)
432436

433-
def __process_rules_diff(self, rule_set: dict[str:str]) -> dict[str:str]:
437+
def rule_severity(self, rule_key: str, substitute_with_default: bool = True) -> str:
438+
"""Returns the severity of a rule in the quality profile
439+
440+
:param str rule_key: The rule key to get severities for
441+
:return: The severity
442+
:rtype: str
443+
"""
444+
return rules.Rule.get_object(self.endpoint, rule_key).rule_severity(self.key, substitute_with_default=substitute_with_default)
445+
446+
def __process_rules_diff(self, rule_set: dict[str, str]) -> list[dict[str, str]]:
434447
diff_rules = {}
435448
for rule in rule_set:
436449
r_key = rule["key"]
437450
diff_rules[r_key] = {}
438-
if self.rule_has_custom_severities(r_key):
439-
diff_rules[r_key]["severities"] = self.rule_impacts(r_key, substitute_with_default=True)
451+
if self.rule_has_custom_severity(r_key):
452+
diff_rules[r_key]["severity"] = self.rule_severity(r_key, substitute_with_default=True)
453+
if self.rule_has_custom_impacts(r_key):
454+
diff_rules[r_key]["impacts"] = {k: v for k, v in self.rule_impacts(r_key, substitute_with_default=True).items() if v != c.DEFAULT}
440455
if self.rule_is_prioritized(r_key):
441456
diff_rules[r_key]["prioritized"] = True
442457
if (params := self.rule_custom_params(r_key)) is not None:
@@ -500,23 +515,44 @@ def used_by_project(self, project: object) -> bool:
500515
"""
501516
return project.key in self.projects()
502517

503-
def rule_has_custom_severities(self, rule_key: str) -> bool:
504-
"""Checks whether the rule has a custom severity in the quality profile
518+
def rule_has_custom_impacts(self, rule_key: str) -> bool:
519+
"""Checks whether the rule has custom impacts in the quality profile
520+
521+
:param str rule_key: The rule key to check
522+
:return: Whether the rule has a some custom impacts in the quality profile
523+
"""
524+
if self.endpoint.is_sonarcloud():
525+
return False
526+
rule = rules.Rule.get_object(self.endpoint, rule_key)
527+
impacts = rule.impacts(quality_profile_id=self.key, substitute_with_default=True)
528+
has_custom = any(sev != c.DEFAULT for sev in impacts.values())
529+
log.debug(
530+
"Checking if rule %s has custom impacts in %s: %s - result %s",
531+
rule_key,
532+
str(self),
533+
str(impacts),
534+
has_custom,
535+
)
536+
return has_custom
537+
538+
def rule_has_custom_severity(self, rule_key: str) -> bool:
539+
"""Checks whether the rule has custom impacts in the quality profile
505540
506541
:param str rule_key: The rule key to check
507-
:return: Whether the rule has a some custom severities in the quality profile
542+
:return: Whether the rule has a some custom severity in the quality profile
508543
"""
509544
if self.endpoint.is_sonarcloud():
510545
return False
511546
rule = rules.Rule.get_object(self.endpoint, rule_key)
547+
sev = rule.rule_severity(quality_profile_id=self.key, substitute_with_default=True)
512548
log.debug(
513-
"Checking if rule %s has custom severities in %s: %s - result %s",
549+
"Checking if rule %s has custom impacts in %s: %s - result %s",
514550
rule_key,
515551
str(self),
516-
str(rule.impacts(quality_profile_id=self.key, substitute_with_default=True)),
517-
any(sev != c.DEFAULT for sev in rule.impacts(quality_profile_id=self.key, substitute_with_default=True).values()),
552+
sev,
553+
sev != c.DEFAULT,
518554
)
519-
return any(sev != c.DEFAULT for sev in rule.impacts(quality_profile_id=self.key, substitute_with_default=True).values())
555+
return sev != c.DEFAULT
520556

521557
def rule_is_prioritized(self, rule_key: str) -> bool:
522558
"""Checks whether the rule is prioritized in the quality profile

sonar/rules.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,8 @@ def impacts(self, quality_profile_id: Optional[str] = None, substitute_with_defa
345345
"""Returns the rule clean code attributes"""
346346
found_qp = None
347347
if quality_profile_id:
348-
self.refresh()
348+
if "actives" not in self.sq_json:
349+
self.refresh()
349350
found_qp = next((qp for qp in self.sq_json.get("actives", []) if quality_profile_id and qp["qProfile"] == quality_profile_id), None)
350351
if not found_qp:
351352
return self._impacts if len(self._impacts) > 0 else {TYPE_TO_QUALITY[self.type]: self.severity}
@@ -361,6 +362,17 @@ def impacts(self, quality_profile_id: Optional[str] = None, substitute_with_defa
361362
else:
362363
return qp_impacts
363364

365+
def rule_severity(self, quality_profile_id: Optional[str] = None, substitute_with_default: bool = True) -> str:
366+
"""Returns the severity, potentially customized in a QP"""
367+
found_qp = None
368+
if quality_profile_id:
369+
if "actives" not in self.sq_json:
370+
self.refresh()
371+
found_qp = next((qp for qp in self.sq_json.get("actives", []) if quality_profile_id and qp["qProfile"] == quality_profile_id), None)
372+
if not found_qp:
373+
return c.DEFAULT if substitute_with_default else self.severity
374+
return c.DEFAULT if substitute_with_default and found_qp["severity"] == self.severity else found_qp["severity"]
375+
364376
def __get_quality_profile_data(self, quality_profile_id: str) -> Optional[dict[str, str]]:
365377
if not quality_profile_id:
366378
return None

0 commit comments

Comments
 (0)