Skip to content

Commit cb6e123

Browse files
authored
Merge pull request #1289 from okorach:improve-yaml-2
Improve YAML output
2 parents f919891 + 3afd3c5 commit cb6e123

File tree

16 files changed

+194
-24
lines changed

16 files changed

+194
-24
lines changed

cli/config.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,34 @@ def __write_export(config: dict[str, str], file: str, format: str) -> None:
108108
"""Writes the configuration in file"""
109109
with utilities.open_file(file) as fd:
110110
if format == "yaml":
111-
print(yaml.dump(config), file=fd)
111+
print(yaml.dump(__convert_for_yaml(config), sort_keys=False), file=fd)
112112
else:
113113
print(utilities.json_dump(config), file=fd)
114114

115115

116+
def __convert_for_yaml(json_export: dict[str, any]) -> dict[str, any]:
117+
"""Converts the default JSON produced by export to a modified version more suitable for YAML"""
118+
if "globalSettings" in json_export:
119+
json_export["globalSettings"] = platform.convert_for_yaml(json_export["globalSettings"])
120+
if "qualityGates" in json_export:
121+
json_export["qualityGates"] = qualitygates.convert_for_yaml(json_export["qualityGates"])
122+
if "qualityProfiles" in json_export:
123+
json_export["qualityProfiles"] = qualityprofiles.convert_for_yaml(json_export["qualityProfiles"])
124+
if "projects" in json_export:
125+
json_export["projects"] = projects.convert_for_yaml(json_export["projects"])
126+
if "portfolios" in json_export:
127+
json_export["portfolios"] = portfolios.convert_for_yaml(json_export["portfolios"])
128+
if "applications" in json_export:
129+
json_export["applications"] = applications.convert_for_yaml(json_export["applications"])
130+
if "users" in json_export:
131+
json_export["users"] = users.convert_for_yaml(json_export["users"])
132+
if "groups" in json_export:
133+
json_export["groups"] = groups.convert_for_yaml(json_export["groups"])
134+
if "rules" in json_export:
135+
json_export["rules"] = rules.convert_for_yaml(json_export["rules"])
136+
return json_export
137+
138+
116139
def __export_config(endpoint: platform.Platform, what: list[str], **kwargs) -> None:
117140
"""Exports a platform configuration in a JSON file"""
118141
export_settings = {

doc/what-is-new.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Next version yet unreleased
22

3+
- `sonar-tools` is now available as a docker image
4+
- `sonar-config` export can now export configuration as a YAML file (Only JSON was available previously). Import of YAML is not yet available
5+
36
# Version 3.3
47

58
- `sonar-config`: Improved / Hardened several elements for both import and export

sonar/applications.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ def export(endpoint: pf.Platform, export_settings: types.ConfigSettings, key_lis
512512
for k in apps_settings:
513513
# remove key from JSON value, it's already the dict key
514514
apps_settings[k].pop("key")
515-
return apps_settings
515+
return dict(sorted(apps_settings.items()))
516516

517517

518518
def audit(endpoint: pf.Platform, audit_settings: types.ConfigSettings, key_list: types.KeyList = None) -> list[problem.Problem]:
@@ -576,3 +576,16 @@ def search_by_name(endpoint: pf.Platform, name: str) -> dict[str, Application]:
576576
data[app.key] = app
577577
# return {app.key: app for app in _OBJECTS.values() if app.name == name}
578578
return data
579+
580+
581+
def convert_for_yaml(original_json: types.ObjectJsonRepr) -> types.ObjectJsonRepr:
582+
"""Convert the original JSON defined for JSON export into a JSON format more adapted for YAML export"""
583+
new_json = util.dict_to_list(original_json, "key")
584+
for app_json in new_json:
585+
app_json["branches"] = util.dict_to_list(app_json["branches"], "name")
586+
for b in app_json["branches"]:
587+
if "projects" in b:
588+
b["projects"] = [{"key": k, "branch": br} for k, br in b["projects"].items()]
589+
if "permissions" in app_json:
590+
app_json["permissions"] = permissions.convert_for_yaml(app_json["permissions"])
591+
return new_json

sonar/devops.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ def to_json(self, export_settings: types.ConfigSettings) -> types.ObjectJsonRepr
153153
:return: The configuration of the DevOps platform (except secrets)
154154
:rtype: dict
155155
"""
156-
json_data = self._json.copy()
157-
json_data.update({"key": self.key, "type": self.type, "url": self.url})
156+
json_data = {"key": self.key, "type": self.type, "url": self.url}
157+
json_data.update(self._json.copy())
158158
return util.filter_export(json_data, _IMPORTABLE_PROPERTIES, export_settings.get("FULL_EXPORT", False))
159159

160160
def set_pat(self, pat, user_name=None):

sonar/groups.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ def export(endpoint: pf.Platform, export_settings: types.ConfigSettings) -> type
267267
"""
268268
log.info("Exporting groups")
269269
g_list = {}
270-
for g_name, g_obj in search(endpoint=endpoint).items():
270+
for g_name, g_obj in sorted(search(endpoint=endpoint).items()):
271271
if not export_settings["FULL_EXPORT"] and g_obj.is_default():
272272
continue
273273
g_list[g_name] = "" if g_obj.description is None else g_obj.description
@@ -365,3 +365,8 @@ def exists(group_name: str, endpoint: pf.Platform) -> bool:
365365
:rtype: bool
366366
"""
367367
return get_object(name=group_name, endpoint=endpoint) is not None
368+
369+
370+
def convert_for_yaml(original_json: types.ObjectJsonRepr) -> types.ObjectJsonRepr:
371+
"""Convert the original JSON defined for JSON export into a JSON format more adapted for YAML export"""
372+
return util.dict_to_list(original_json, "name", "description")

sonar/permissions/permissions.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,3 +382,12 @@ def black_list(perms: types.JsonPermissions, disallowed_perms: list[str]) -> typ
382382
for user_or_group, original_perms in sub_perms.items():
383383
resulting_perms[perm_type][user_or_group] = [p for p in original_perms if p not in disallowed_perms]
384384
return resulting_perms
385+
386+
387+
def convert_for_yaml(json_perms: types.ObjectJsonRepr) -> types.ObjectJsonRepr:
388+
"""Converts permissions in a format that is more friendly for YAML"""
389+
converted_perms = []
390+
for ptype in "groups", "users":
391+
if ptype in json_perms:
392+
converted_perms += utilities.dict_to_list(json_perms[ptype], ptype[:-1], "permissions")
393+
return converted_perms

sonar/platform.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,3 +820,19 @@ def _check_for_retry(response: requests.models.Response) -> tuple[bool, str]:
820820
log.debug("Moved permanently to URL %s", new_url)
821821
return True, new_url
822822
return False, None
823+
824+
825+
def convert_for_yaml(original_json: types.ObjectJsonRepr) -> types.ObjectJsonRepr:
826+
"""Convert the original JSON defined for JSON export into a JSON format more adapted for YAML export"""
827+
if "languages" in original_json:
828+
original_json["languages"] = util.dict_to_list(original_json["languages"], "language")
829+
if "permissions" in original_json:
830+
original_json["permissions"] = permissions.convert_for_yaml(original_json["permissions"])
831+
if "permissionTemplates" in original_json:
832+
for tpl in original_json["permissionTemplates"].values():
833+
if "permissions" in tpl:
834+
tpl["permissions"] = permissions.convert_for_yaml(tpl["permissions"])
835+
original_json["permissionTemplates"] = util.dict_to_list(original_json["permissionTemplates"], "name")
836+
if "devopsIntegration" in original_json:
837+
original_json["devopsIntegration"] = util.dict_to_list(original_json["devopsIntegration"], "name")
838+
return original_json

sonar/portfolios.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -336,26 +336,24 @@ def audit(self, audit_settings: types.ConfigSettings) -> list[problem.Problem]:
336336
def to_json(self, export_settings: types.ConfigSettings) -> types.ObjectJsonRepr:
337337
"""Returns the portfolio representation as JSON"""
338338
self.refresh()
339-
json_data = {}
339+
json_data = {"key": self.key, "name": self.name}
340+
if self._description:
341+
json_data["description"] = self._description
340342
subportfolios = self.sub_portfolios()
343+
if not self.is_sub_portfolio:
344+
json_data["visibility"] = self._visibility
345+
json_data["permissions"] = self.permissions().export(export_settings=export_settings)
346+
json_data["tags"] = self._tags
341347
if subportfolios:
342348
json_data["portfolios"] = {}
343349
for s in subportfolios.values():
344350
subp_json = s.to_json(export_settings)
345351
subp_key = subp_json.pop("key")
346352
json_data["portfolios"][subp_key] = subp_json
347-
if not self.is_sub_portfolio:
348-
json_data["permissions"] = self.permissions().export(export_settings=export_settings)
349-
json_data["visibility"] = self._visibility
350-
json_data["key"] = self.key
351-
json_data["name"] = self.name
352-
json_data["tags"] = self._tags
353-
json_data["applications"] = self._applications
354-
if self._description:
355-
json_data["description"] = self._description
356353
mode = self.selection_mode().copy()
357354
if mode and "none" not in mode:
358355
json_data["projects"] = mode
356+
json_data["applications"] = self._applications
359357
return json_data
360358

361359
def export(self, export_settings: types.ConfigSettings) -> types.ObjectJsonRepr:
@@ -744,7 +742,7 @@ def export(endpoint: pf.Platform, export_settings: types.ConfigSettings, key_lis
744742
nb_portfolios = count(endpoint=endpoint)
745743
i = 0
746744
exported_portfolios = {}
747-
for k, p in get_list(endpoint=endpoint, key_list=key_list).items():
745+
for k, p in sorted(get_list(endpoint=endpoint, key_list=key_list).items()):
748746
if not p.is_sub_portfolio:
749747
exported_portfolios[k] = p.export(export_settings)
750748
exported_portfolios[k].pop("key")
@@ -797,3 +795,20 @@ def __create_portfolio_hierarchy(endpoint: pf.Platform, data: types.ApiPayload,
797795
def get_api_branch(branch: str) -> str:
798796
"""Returns the value to pass to the API for the branch parameter"""
799797
return branch if branch != settings.DEFAULT_BRANCH else None
798+
799+
800+
def convert_for_yaml(original_json: types.ObjectJsonRepr) -> types.ObjectJsonRepr:
801+
"""Convert the original JSON defined for JSON export into a JSON format more adapted for YAML export"""
802+
new_json = util.dict_to_list(original_json, "key")
803+
for p_json in new_json:
804+
try:
805+
p_json["projects"] = [{"key": k, "branch": br} for k, br in p_json["projects"]["manual"].items()]
806+
except KeyError:
807+
pass
808+
if "portfolios" in p_json:
809+
p_json["portfolios"] = convert_for_yaml(p_json["portfolios"])
810+
if "applications" in p_json:
811+
p_json["applications"] = [{"key": k, "branches": br} for k, br in p_json["applications"].items()]
812+
if "permissions" in p_json:
813+
p_json["permissions"] = perms.convert_for_yaml(p_json["permissions"])
814+
return new_json

sonar/projects.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1385,7 +1385,7 @@ def export(endpoint: pf.Platform, export_settings: types.ConfigSettings, key_lis
13851385
worker.setName(f"ProjectExport{i}")
13861386
worker.start()
13871387
q.join()
1388-
return project_settings
1388+
return dict(sorted(project_settings.items()))
13891389

13901390

13911391
def exists(key: str, endpoint: pf.Platform) -> bool:
@@ -1489,3 +1489,22 @@ def export_zip(endpoint: pf.Platform, key_list: types.KeyList = None, threads: i
14891489
},
14901490
"project_exports": exports,
14911491
}
1492+
1493+
1494+
def convert_proj_for_yaml(proj_json: types.ObjectJsonRepr) -> types.ObjectJsonRepr:
1495+
"""Convert the original JSON defined for JSON export into a JSON format more adapted for YAML export"""
1496+
if "branches" in proj_json:
1497+
proj_json["branches"] = util.dict_to_list(proj_json["branches"], "name")
1498+
if "qualityProfiles" in proj_json:
1499+
proj_json["qualityProfiles"] = util.dict_to_list(proj_json["qualityProfiles"], "language", "name")
1500+
if "permissions" in proj_json:
1501+
proj_json["permissions"] = perms.convert_for_yaml(proj_json["permissions"])
1502+
return proj_json
1503+
1504+
1505+
def convert_for_yaml(original_json: types.ObjectJsonRepr) -> types.ObjectJsonRepr:
1506+
"""Convert the original JSON defined for JSON export into a JSON format more adapted for YAML export"""
1507+
new_json = []
1508+
for proj in util.dict_to_list(original_json, "key"):
1509+
new_json.append(convert_proj_for_yaml(proj))
1510+
return new_json

sonar/qualitygates.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ def export(endpoint: pf.Platform, export_settings: types.ConfigSettings) -> type
380380
:rtype: dict
381381
"""
382382
log.info("Exporting quality gates")
383-
return {k: qg.to_json(export_settings) for k, qg in get_list(endpoint).items()}
383+
return {k: qg.to_json(export_settings) for k, qg in sorted(get_list(endpoint).items())}
384384

385385

386386
def import_config(endpoint: pf.Platform, config_data: types.ObjectJsonRepr) -> bool:
@@ -465,3 +465,8 @@ def _decode_condition(c: str) -> tuple[str, str, str]:
465465
def search_by_name(endpoint: pf.Platform, name: str) -> dict[str, QualityGate]:
466466
"""Searches quality gates matching name"""
467467
return util.search_by_name(endpoint, name, APIS["list"], "qualitygates")
468+
469+
470+
def convert_for_yaml(original_json: types.ObjectJsonRepr) -> types.ObjectJsonRepr:
471+
"""Convert the original JSON defined for JSON export into a JSON format more adapted for YAML export"""
472+
return util.dict_to_list(original_json, "name")

0 commit comments

Comments
 (0)