diff --git a/CHANGELOG.md b/CHANGELOG.md index a54cba09..fb938d32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * feat: Add support for hiding empty plays and plays without roles https://github.com/haidaraM/ansible-playbook-grapher/pull/177. * Add a new flag `--hide-empty-plays` to not show in the graph the plays that end up being empty after applying the filters. * Add a new flag `--hide-plays-without-roles` to not show in the graph the plays that end up with no roles. Only roles at the play level and include_role as tasks are considered (no import_role). +* Add support for viewing mermaid graphs in the browse with `--view --renderer mermaid-flowchart` in https://github.com/haidaraM/ansible-playbook-grapher/pull/181 * refactor(internal): `PlaybookNode.plays` is now a method instead of property. * refactor(internal): Do not access the `_compositions` in the child classes: use method from the CompositeNode. * chore(deps): update black requirement from ~=24.1 to ~=24.2 by @dependabot in https://github.com/haidaraM/ansible-playbook-grapher/pull/175 diff --git a/ansibleplaybookgrapher/renderer/mermaid.py b/ansibleplaybookgrapher/renderer/mermaid.py index c24a33de..cfed1432 100644 --- a/ansibleplaybookgrapher/renderer/mermaid.py +++ b/ansibleplaybookgrapher/renderer/mermaid.py @@ -14,6 +14,10 @@ # along with this program. If not, see . from pathlib import Path +import json +import zlib +from base64 import urlsafe_b64encode +import webbrowser from typing import Dict, Set, List from ansible.utils.display import Display @@ -113,14 +117,39 @@ def render( ) if view: - # TODO: implement the view option - # https://github.com/mermaidjs/mermaid-live-editor/issues/41 and https://mermaid.ink/ - display.warning( - "The --view option is not supported yet by the mermaid renderer" - ) + MermaidFlowChartRenderer.view(mermaid_code) return str(final_output_path_file) + @staticmethod + def view(mermaid_code: str): + """ + View the mermaid code in the browser using https://mermaid.live/ + + This is based on: + - https://github.com/mermaid-js/mermaid-live-editor/blob/b5978e6faf7635e39452855fb4d062d1452ab71b/src/lib/util/serde.ts#L19-L29 + - https://github.com/mermaidjs/mermaid-live-editor/issues/41#issuecomment-1820242778 + + :param mermaid_code: + :return: + """ + graph_state = { + "code": mermaid_code, + "mermaid": {"theme": "default"}, + "autoSync": True, + "updateDiagram": True, + } + + compressed = zlib.compress(json.dumps(graph_state).encode("utf-8"), level=9) + + url_path = f'pako:{urlsafe_b64encode(compressed).decode("utf-8")}' + url = f"https://mermaid.live/edit#{url_path}" + + display.display(f"Mermaid live editor URL: {url}") + + # Display url using the default browser in a new tag + webbrowser.open(url, new=2) + class MermaidFlowChartPlaybookBuilder(PlaybookBuilder): def __init__( diff --git a/tests/test_mermaid_renderer.py b/tests/test_mermaid_renderer.py index a4c41260..9c0e1470 100644 --- a/tests/test_mermaid_renderer.py +++ b/tests/test_mermaid_renderer.py @@ -27,6 +27,9 @@ def run_grapher( # Explicitly add verbosity to the tests additional_args.insert(0, "-vvv") + if os.environ.get("TEST_VIEW_GENERATED_FILE") == "1": + additional_args.insert(0, "--view") + playbook_paths = [os.path.join(FIXTURES_DIR, p_file) for p_file in playbook_files] args = [__prog__] @@ -59,11 +62,18 @@ def _common_tests(mermaid_path: str, playbook_paths: List[str], **kwargs): # TODO: add proper tests on the mermaid code. # Need a parser to make sure the outputs contain all the playbooks, plays, tasks and roles - # test if the file exist. It will exist only if we write in it. + # Test if the file exist. It will exist only if we write in it. assert os.path.isfile( mermaid_path ), f"The mermaid file should exist at '{mermaid_path}'" + with open(mermaid_path, "r") as mermaid_file: + mermaid_data = mermaid_file.read() + for playbook_path in playbook_paths: + assert ( + playbook_path in mermaid_data + ), "The playbook path should be in the generated code" + @pytest.mark.parametrize( "playbook_file",