From 99903dfc66ad28f2ca3a341b6167f058e3a23436 Mon Sep 17 00:00:00 2001 From: Colin Dean Date: Sat, 2 Sep 2023 19:16:11 +0000 Subject: [PATCH 1/4] Adds support for Mermaid diagrams in ADRs Create a codeblock with mermaid as the language tag and it'll render. See the new test ADR for an example. --- adr_viewer/__init__.py | 10 +++- adr_viewer/templates/index.html | 32 ++++++++++++- adr_viewer/test_adr_viewer.py | 28 +++++++++++ test/adr/0005-has-mermaid.md | 82 +++++++++++++++++++++++++++++++++ 4 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 test/adr/0005-has-mermaid.md diff --git a/adr_viewer/__init__.py b/adr_viewer/__init__.py index 7a547cb..e065178 100644 --- a/adr_viewer/__init__.py +++ b/adr_viewer/__init__.py @@ -47,11 +47,14 @@ def parse_adr_to_config(path) -> Optional[Dict]: header = soup.find('h1') + includes_mermaid = soup.find(name='code', attrs={'class': 'language-mermaid'}) + if header: return { 'status': status, 'body': adr_as_html, - 'title': header.text + 'title': header.text, + 'includes_mermaid': includes_mermaid } else: return None @@ -88,7 +91,8 @@ def generate_content(path, template_dir_override=None, title=None) -> str: config = { 'project_title': title if title else os.path.basename(os.getcwd()), - 'records': [] + 'records': [], + 'include_mermaid': False, } for index, adr_file in enumerate(files): @@ -97,6 +101,8 @@ def generate_content(path, template_dir_override=None, title=None) -> str: if adr_attributes: adr_attributes['index'] = index + if not config['include_mermaid']: + config['include_mermaid'] = bool(adr_attributes['includes_mermaid']) config['records'].append(adr_attributes) else: diff --git a/adr_viewer/templates/index.html b/adr_viewer/templates/index.html index 6ee5e72..01490dc 100644 --- a/adr_viewer/templates/index.html +++ b/adr_viewer/templates/index.html @@ -6,6 +6,9 @@ + {% if config.include_mermaid %} + + {% endif %} ADR Viewer - {{ config.project_title }} @@ -94,7 +97,34 @@

{% endfor %}
- Generated with <3 using ADR Viewer + Generated with <3 using ADR Viewer
+ {% if config.include_mermaid %} + + {% endif %} diff --git a/adr_viewer/test_adr_viewer.py b/adr_viewer/test_adr_viewer.py index 36c6f23..b260462 100644 --- a/adr_viewer/test_adr_viewer.py +++ b/adr_viewer/test_adr_viewer.py @@ -90,3 +90,31 @@ def test_should_ignore_invalid_files(): config = parse_adr_to_config('test/adr/0003-bad-formatting.md') assert config is None + + +def test_should_detect_mermaid(): + config = parse_adr_to_config('test/adr/0005-has-mermaid.md') + + assert config['includes_mermaid'] + + +def test_should_not_detect_mermaid(): + config = parse_adr_to_config('test/adr/0004-proposed-status.md') + + assert not config['includes_mermaid'] + + +def test_should_render_html_with_mermaid(): + html = render_html({ + 'include_mermaid': True + }) + + assert 'mermaid.min.js' in html + + +def test_should_render_html_without_mermaid(): + html = render_html({ + 'include_mermaid': False + }) + + assert 'mermaid.min.js' not in html \ No newline at end of file diff --git a/test/adr/0005-has-mermaid.md b/test/adr/0005-has-mermaid.md new file mode 100644 index 0000000..fe72913 --- /dev/null +++ b/test/adr/0005-has-mermaid.md @@ -0,0 +1,82 @@ +# 2. Use mermaid for diagrams + +Date: 2023-09-02 + +## Status + +Accepted + +## Context + +We need to be able to diagram things. + +## Decision + +We will use [Mermaid](https://mermaid.js.org). + +```mermaid +journey + title Using Mermaid in adr-viewer + section Writing ADR + Learn ADR: 5: Me + Write ADR: 3: Me + Render ADR: 6: Me +``` + +```mermaid +flowchart TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[Car] +``` + +```mermaid +sequenceDiagram + Alice->>+John: Hello John, how are you? + Alice->>+John: John, can you hear me? + John-->>-Alice: Hi Alice, I can hear you! + John-->>-Alice: I feel great! +``` + +```mermaid +classDiagram + Animal <|-- Duck + Animal <|-- Fish + Animal <|-- Zebra + Animal : +int age + Animal : +String gender + Animal: +isMammal() + Animal: +mate() + class Duck{ + +String beakColor + +swim() + +quack() + } + class Fish{ + -int sizeInFeet + -canEat() + } + class Zebra{ + +bool is_wild + +run() + } +``` + + +## Consequences + +We need to add a Mermaid script in the `head` tag of the template, potentially only when there is a Mermaid block in a Markdown document. + +```mermaid +gantt + title A Gantt Diagram + dateFormat YYYY-MM-DD + section Section + A task :a1, 2014-01-01, 30d + Another task :after a1 , 20d + section Another + Task in sec :2014-01-12 , 12d + another task : 24d +``` \ No newline at end of file From 91eae4fce2c42ad836c22e587bf37a28a4316f28 Mon Sep 17 00:00:00 2001 From: Colin Dean Date: Sat, 2 Sep 2023 19:20:42 +0000 Subject: [PATCH 2/4] Mention Mermaid in the README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6db4af4..b1480e8 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![Latest Version](https://img.shields.io/pypi/v/adr-viewer)](https://pypi.org/project/adr-viewer/) Show off your Architecture Decision Records with an easy-to-navigate web page, either as a local web-server or generated static content. +Include diagrams in [Mermaid](https://mermaid.js.org) format using code blocks/fences, e.g. [test ADR 5](test/adr/0005-has-mermaid.md). ## Examples From 3035a690cc4d160bdd2e70896f502fdf1938abda Mon Sep 17 00:00:00 2001 From: Colin Dean Date: Fri, 29 Sep 2023 14:49:05 -0400 Subject: [PATCH 3/4] Reformats with Black --- adr_viewer/__init__.py | 4 ++-- adr_viewer/parse.py | 4 ++-- adr_viewer/test_parse.py | 4 ++-- adr_viewer/test_render.py | 12 ++++-------- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/adr_viewer/__init__.py b/adr_viewer/__init__.py index 3782d1f..b30524d 100644 --- a/adr_viewer/__init__.py +++ b/adr_viewer/__init__.py @@ -30,8 +30,8 @@ def generate_content(path, template_dir_override=None, title=None) -> str: if adr_attributes: adr_attributes.index = index - if not config['include_mermaid']: - config['include_mermaid'] = bool(adr_attributes['includes_mermaid']) + if not config["include_mermaid"]: + config["include_mermaid"] = bool(adr_attributes["includes_mermaid"]) config["records"].append(adr_attributes) else: diff --git a/adr_viewer/parse.py b/adr_viewer/parse.py index 1e12d2f..1686db7 100644 --- a/adr_viewer/parse.py +++ b/adr_viewer/parse.py @@ -53,14 +53,14 @@ def parse_adr(content: str) -> Optional[Adr]: header = soup.find("h1") - includes_mermaid = soup.find(name='code', attrs={'class': 'language-mermaid'}) + includes_mermaid = soup.find(name="code", attrs={"class": "language-mermaid"}) if header: return Adr( title=header.text, status=status, body=adr_as_html, - includes_mermaid=includes_mermaid + includes_mermaid=includes_mermaid, ) else: return None diff --git a/adr_viewer/test_parse.py b/adr_viewer/test_parse.py index 4b12d12..88880ef 100644 --- a/adr_viewer/test_parse.py +++ b/adr_viewer/test_parse.py @@ -65,14 +65,14 @@ def test_should_ignore_invalid_files(): def test_should_detect_mermaid(): - markdown = open('test/adr/0005-has-mermaid.md').read() + markdown = open("test/adr/0005-has-mermaid.md").read() adr = parse_adr(markdown) assert adr.includes_mermaid def test_should_not_detect_mermaid(): - markdown = open('test/adr/0004-proposed-status.md').read() + markdown = open("test/adr/0004-proposed-status.md").read() adr = parse_adr(markdown) assert not adr.includes_mermaid diff --git a/adr_viewer/test_render.py b/adr_viewer/test_render.py index 189097a..fdf2ad6 100644 --- a/adr_viewer/test_render.py +++ b/adr_viewer/test_render.py @@ -33,16 +33,12 @@ def test_should_render_html_with_collapsible_index(): def test_should_render_html_with_mermaid(): - html = render_html({ - 'include_mermaid': True - }) + html = render_html({"include_mermaid": True}) - assert 'mermaid.min.js' in html + assert "mermaid.min.js" in html def test_should_render_html_without_mermaid(): - html = render_html({ - 'include_mermaid': False - }) + html = render_html({"include_mermaid": False}) - assert 'mermaid.min.js' not in html + assert "mermaid.min.js" not in html From 259e0f9db83705b2fe914f8e54f7935ab2212946 Mon Sep 17 00:00:00 2001 From: Colin Dean Date: Fri, 29 Sep 2023 14:51:14 -0400 Subject: [PATCH 4/4] Fix a mypy warning resulting from introduction of Adr class --- adr_viewer/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adr_viewer/__init__.py b/adr_viewer/__init__.py index b30524d..69edf66 100644 --- a/adr_viewer/__init__.py +++ b/adr_viewer/__init__.py @@ -31,7 +31,7 @@ def generate_content(path, template_dir_override=None, title=None) -> str: if adr_attributes: adr_attributes.index = index if not config["include_mermaid"]: - config["include_mermaid"] = bool(adr_attributes["includes_mermaid"]) + config["include_mermaid"] = adr_attributes.includes_mermaid config["records"].append(adr_attributes) else: