Skip to content

Commit 4fa1772

Browse files
authored
feat: Generate a Graphviz DOT diagram of an Organization (#13)
### Added - Adds support for generating a Graphviz diagram of an Organization with the new `OrganizationDataBuilder.to_dot()` function - Adds `DOT` as a supported output format for the `organization dump-all` command ### Changed - breaking: Renames `organization dump-json` CLI command to `organization dump-all`
1 parent 848e0c4 commit 4fa1772

File tree

5 files changed

+72
-12
lines changed

5 files changed

+72
-12
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ the unreleased section to the section for the new release.
1414

1515
No unreleased changes.
1616

17+
### Added
18+
19+
- Adds support for generating a Graphviz diagram of an Organization with the new
20+
`OrganizationDataBuilder.to_dot()` function
21+
- Adds `DOT` as a supported output format for the `organization dump-all` command
22+
23+
### Changed
24+
25+
- breaking: Renames `organization dump-json` CLI command to `organization dump-all`
26+
1727
## [0.1.0-beta2] - 2021-06-16
1828

1929
### Added

aws_data_tools/builders/organizations.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from dataclasses import dataclass, field, InitVar
66
from typing import Any, Dict, List, Union
77

8+
from graphviz import Digraph, unflatten
9+
810
from ..client import APIClient
911
from ..models.base import ModelBase
1012
from ..utils import query_tags
@@ -78,6 +80,34 @@ def api(self, func: str, **kwargs) -> Union[List[Dict[str, Any]], Dict[str, Any]
7880
self.Connect()
7981
return self.client.api(func, **kwargs)
8082

83+
def to_dot(self) -> str:
84+
"""Return the organization as a GraphViz DOT diagram"""
85+
graph = Digraph("Organization", filename="organization.dot")
86+
nodes = []
87+
nodes.append(self.dm.root)
88+
nodes.extend(self.dm.organizational_units)
89+
nodes.extend(self.dm.accounts)
90+
for node in nodes:
91+
if getattr(node, "parent", None) is None:
92+
continue
93+
shape = None
94+
if isinstance(node, Root):
95+
shape = "circle"
96+
elif isinstance(node, OrganizationalUnit):
97+
shape = "box"
98+
elif isinstance(node, Account):
99+
shape = "ellipse"
100+
else:
101+
continue
102+
graph.node(node.id, label=node.name, shape=shape)
103+
graph.edge(node.parent.id, node.id)
104+
return unflatten(
105+
graph.source,
106+
stagger=10,
107+
fanout=10,
108+
chain=10,
109+
)
110+
81111
def __e_organization(self) -> Dict[str, str]:
82112
"""Extract org description data from the DescribeOrganization API"""
83113
return self.api("describe_organization").get("organization")

aws_data_tools/cli/__init__.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ def handle_error(ctx, err_msg, tb=None):
6464

6565

6666
@organization.command(short_help="Dump org data as JSON")
67+
@option(
68+
"--format",
69+
"-f",
70+
"format_",
71+
default="JSON",
72+
type=Choice(["DOT", "JSON", "YAML"], case_sensitive=False),
73+
help="The output format for the data",
74+
)
6775
@option(
6876
"--no-accounts",
6977
default=False,
@@ -76,24 +84,16 @@ def handle_error(ctx, err_msg, tb=None):
7684
is_flag=True,
7785
help="Exclude policy data from the model",
7886
)
79-
@option(
80-
"--format",
81-
"-f",
82-
"format_",
83-
default="JSON",
84-
type=Choice(["JSON", "YAML"], case_sensitive=False),
85-
help="The output format for the data",
86-
)
8787
@option("--out-file", "-o", help="File path to write data instead of stdout")
8888
@pass_context
89-
def dump_json(
89+
def dump_all(
9090
ctx: Dict[str, Any],
91+
format_: str,
9192
no_accounts: bool,
9293
no_policies: bool,
93-
format_: str,
9494
out_file: str,
9595
) -> None:
96-
"""Dump a JSON representation of the organization"""
96+
"""Dump a data representation of the organization"""
9797
err_msg = None
9898
tb = None
9999
try:
@@ -114,6 +114,8 @@ def dump_json(
114114
s_func = odb.to_json
115115
elif format_ == "YAML":
116116
s_func = odb.to_yaml
117+
elif format_ == "DOT":
118+
s_func = odb.to_dot
117119
if out_file is None:
118120
out_file = "-"
119121
with open_file(out_file, mode="wb") as f:

poetry.lock

Lines changed: 18 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ mkdocs = {version = "^1.1.2", optional = true, extras = ["docs"]}
4848
mkdocs-git-revision-date-localized-plugin = {version = "^0.9.2", optional = true, extras = ["docs"]}
4949
mkdocs-macros-plugin = {version = "^0.5.5", optional = true, extras = ["docs"]}
5050
mkdocs-material = {version = "^7.1.5", optional = true, extras = ["docs"]}
51+
graphviz = "^0.16"
5152

5253
[tool.poetry.dev-dependencies]
5354
black = "^21.5b1"

0 commit comments

Comments
 (0)