Power Automate フローをもとに Mermaid API で画像化したフローチャート図を作成する

# 1. はじめに
:::note warn
注意
この記事は**アイデア段階**での投稿になります。
後述します一部の形式（制御構造）には対応していないため、ご注意ください。
:::

このフローでは、作成済みのPower Automate フローを取得して、画像化した単純なフローチャート図を作成するフローです。

例えばこんな感じです。
- Power Automate フロー
![2024-01-28_11h23_16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/81bb6c33-13b7-3251-0b5b-1ebda984c970.png)
- 画像化したフロー(OneDriveに保存)
![2024-01-28_11h22_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/986bcade-c694-4900-84de-724aac97d413.png)

---
分岐のあるフローも画像化できます
- Power Automate フロー
![2024-01-28_11h23_39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/704b0536-b2a7-2d13-ff1c-862878396c19.png)

- 画像化したフロー(OneDriveに保存)
![2024-01-28_11h23_51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/93c5bca0-5610-1515-a737-e8b930c15f9a.png)

# 2. このフローではまだできないこと
入れ子となっているような複雑に構造化されたフローはまだ取得できず、外側の構造のみを取得します。
そのため、スコープや繰り返しの中のアクションは取得できません。
こちらについてはアイデアはあるものの、1つの記事にまとめるのは封雑なため、今後の課題としておきます。

<details><summary>長いので折りたたみます</summary>

- Power Automate フロー
![make.powerautomate.com_environments_Default-b794cd83-44cc-447c-856e-a27e55624c65_flows_61195529-bb64-4836-8f1f-ef21bb7b2fa3_v3=true(PowerAutomate).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/a81adac4-e2dd-9c3a-85af-c33e6b4ddf71.png)

- 画像化したフロー(OneDriveに保存)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/bfbf3624-366f-1171-0f64-232a396e299c.png)

</details>

# 3. Mermaid について
フローチャートを画像化するために使っているMermaid について、簡単に説明しておきます。

詳しくはこちらの記事を参考にさせていただきました。

https://qiita.com/b-mente/items/97a4296666faccd53a72

## 3-1. Mermaid とは？
以下はCopilotに生成してもらった説明文です。
> Mermaidは、特定の記法で記述したテキストから図を生成するツールです。このツールを利用すると、コードで簡単に様々な図を作成できます。主要なサービスやツール、例えばGithubやNotion、VS CodeなどがMermaid記法をサポートしています。
> 
> Mermaidでは以下のような種類の図を生成することができます：
> - フローチャート
> - シーケンス図
> - クラス図
> - 状態遷移図
> - ER図
> - ガントチャート
> - ユーザジャーニー
> - パイチャート
> - その他
> 
> これらの図は、Mermaidの記法に従ってコードで記述し、それを元に図が生成されます。これにより、図を手書きする必要がなく、コードで管理できるようになります。これは、ドキュメンテーションの作成や情報の視覚化に非常に便利です。また、MermaidはJavaScriptをベースとしているため、Webページに組み込んで動的に図を生成することも可能です。

## 3-2. このフローでの使い道
要するに、決められた形式のテキストを図にしてくれるツールですので、例えばフローの詳細からこういったテキストを生成できれば、画像を作成できるということになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/4ecb5fe3-0fed-728e-4cde-5b2b6e30b9f1.png)

:::note info
ちなみにQiita記事の中でも書ける
```mermaid
graph TB
manual-->作成
変数を初期化する-->終了
manual-->現在の時刻
作成-->変数を初期化する
現在の時刻-->変数を初期化する
変数を初期化する-->変数の設定
```
↑
```plaintext:mermaid
```mermaid
graph TB
manual-->作成
変数を初期化する-->終了
manual-->現在の時刻
作成-->変数を初期化する
現在の時刻-->変数を初期化する
変数を初期化する-->変数の設定
```
:::

## 3-3. 画像化のためのAPI
画像として出力するために、こちらのサービスを利用します。

https://mermaid.ink/

使用方法など、詳しくは上記ページを参照していただきたいのですが、`https://mermaid.ink/img/`に続けてbase64に変換したMermaid記法のテキストを入力してできたURLにアクセスすると、画像を取得することができます。

https://miseruit.com/2023/02/23/post-4274/

# 4. フローの解説
ということで、Power Automate フローの中でフローの詳細をMermaid記法で記述し、先のAPIから画像を取得してみたのが今回の内容です。
ちなみに、プレミアムコネクタは使用せず、標準コネクタのみで実現可能です！

<details><summary>フローの全体像</summary>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/ea8a31f6-fcb8-1e99-33d4-ddcf203bd781.png)

</details>

## 4-1. 結果を記録するための変数
最初に、Mermaid記法でフローの中身を記録していくための**文字列変数**を定義します。
初期値としてフローチャート形式であり、上から下方向に記述するという意味の`graph TB`を設定しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/b2f7e5de-92a3-5fe1-48c7-6b063e058566.png)

## 4-2. フローの取得
次にフローの定義を取得するためのアクションを実行します。
これによって、フローにどんなアクションが設定されていて、どんな順序になっているかを確認することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/69e677fa-d5d7-85be-d43b-364c6ab19cf7.png)

## 4-3. トリガー名の取得
取得したフロー定義のうち、トリガーはこのようなオブジェクトで取得できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/ed1025fe-3e3d-80f6-f603-f419e58eada8.png)

この`manual`というのが手動トリガーを意味するトリガー名なのですが、このトリガー名は値ではなくキーになっているため、単純に取得することができません。

**Xpath関数**を使えばいい感じに取得できそうな気がしますが、あまり詳しくなくどうにもできなかったため、単純にダブルクォーテーションで区切った値を取得するという方法を取りました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/3fe2af1a-e66b-59a9-c1d1-ec41c1c19563.png)

```powerfx:trigger
split(
    string(
        outputs('フローの取得')?['body/properties/definition/triggers']
    ),
    '"'
)?[1]
```

## 4-4. アクション名の取得
取得したフロー定義のうち、アクションはこのようなオブジェクトで取得できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/bfbee2bd-e6b4-f3a1-8b7b-358961ea3ae8.png)


これもトリガーと同じように、キーで表現されているため、簡単にアクション名を取得することができません。

さらに、トリガーとは違って項目が複数あるため、すべてのアクション名を取り出すにはさらに工夫が必要です。

自力でのキーの取得は複雑なロジックが必要になるため、既存のアクションを使って省力化します。

`CSVテーブルの作成`アクションに、先ほど取得したアクションの一覧を入力します。
ただし、この`CSVテーブルの作成`アクションは配列のみを受け付けるため、`[]`で囲って配列化しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/eaba916c-af1e-f1e2-5ba5-4e5503b9d255.png)

作成した結果がこちらです。
重要なのは、キーだったアクション名が、列の見出しとして1行目にまとまっているところです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/02b08858-6cce-24ea-2aa0-d3d4b7cda546.png)

この結果を改行で区切って1行目のみを取り出し、さらにカンマで区切ることでそれぞれのアクション名が取得できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/0f270705-ea04-1408-f16d-a0f4b236f83f.png)

```powerfx:actions
split(
    split(
        body('CSV_テーブルの作成'),
        decodeUriComponent('%0D%0A')
    )?[0],
    ','
)
```

## 4-5. アクションごとにループ
取得できたアクションごとにループを回します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/8a06110d-6ebe-1e30-0303-8016c278a6ac.png)

ループの対象になっているのはアクション名ですが、アクション全体を取り出すために、アクション名をキーとしてオブジェクトを取り出します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/21d0a7c6-4aa6-f0f3-6711-f72cb8f73edf.png)
```powerfx:action
outputs('フローの取得')?['body/properties/definition/actions']?[item()]
```
`item()`というのが、今のループで使われているアクション名です。
取り出した結果は以下のようなオブジェクトです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/a06efdd2-60c8-a83d-836f-56211fbcec7b.png)


## 4-6. ひとつ前のアクションの取得
先ほど取り出した`action`のうち、`runAfter`で定義されているのがひとつ前のアクションです。
    今ループを回しているアクション名と、ひとつ前のアクション名を取得して、文字列変数にMermaid記法でフローの定義をメモしていきます。
```text:メモしたい内容
ひとつ前のアクション名-->今のアクション名
```

### 4-6-1. ひとつ前のアクションがない場合
トリガーの直後やスコープの中の最初のアクションなど、その階層での最初のアクションでは、この`runAfter`が空になっています。
空になっているとアクション名が取得できないので、条件分岐で空かどうかを確認し、空の場合は先に取得しておいたトリガー名をひとつ前のアクションとして扱います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/40d35db1-e049-4519-7e49-f3b9f944e9bc.png)

```powerfx:条件
empty(outputs('action')?['runAfter'])
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/69efa066-1e8b-7b42-b7c6-38549866c1da.png)

```powerfx:文字列変数に追加

@{outputs('triggerName')}-->@{outputs('actionName')}
```
※新しい行にメモするため、最初に改行しているのがポイントです。

### 4-6-2. ひとつ前のアクションがある場合
前のアクションがある場合は、単純にそのアクション名を使えばOKです。
ただし、分岐がある場合など、1つ前のアクションが複数存在する場合があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/360cf60e-cde4-6869-d0cb-17295f5f1229.png)

そのため、アクション名を取得したのと同様の方法で、1つ前のアクションをすべて取得し、それぞれに対して処理を実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/3687d436-c48a-c642-073c-8ab33643c20f.png)


## 4-7. 画像化のためのURLの作成準備
フローの定義が取得し終わったので、最後に画像を取得していきます。
うまくいっていれば、文字列変数の中身はこのようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/0dd904ba-54db-b58c-7a5d-058170794efc.png)

これをBase64に変換します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/17409511-2bd2-c4f2-cc5e-ac64b0e21f5e.png)

```powerfx:base64
base64(variables('mermaid'))
```

しかし、Base64形式のテキストには`/`が混ざる可能性があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/a63e9c2b-b648-6752-0637-c440f82fe89c.png)

https://ja.wikipedia.org/wiki/Base64#%E5%A4%89%E6%8F%9B%E8%A1%A8:~:text=111111,/

URLとしては特別な意味を持つ記号であり、そのままでは使えないため、アンダーバー`_`に置換する必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/dd730ff2-a739-9e9d-6335-bfc205a237f0.png)


```powerfx:置換
replace(outputs('base64'),'/','_')
```

## 4-8. URLにアクセスして画像を取得
URLのために必要な要素が作成できたので、実際に画像を取得して保存します。

認証の必要のないAPIや単純なWebコンテンツであれば、OneDriveコネクタの`URL からのファイルのアップロード`アクションを利用することで結果を取得することが可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2572227/103a7b59-6adb-2034-7fc4-97276b132109.png)

URLのみで定義できる、疑似的なHTTPリクエストアクションとして使用することが可能です。

```text:ソース URL
https://mermaid.ink/img/@{outputs('置換')}
```

保存場所やファイル名は、取得したフローの表示名などを使って適当に設定しています。
```text:対象ファイルのパス
/フロー図/@{outputs('フローの取得')?['body/properties/displayName']}_@{outputs('フローの取得')?['body/name']}.jpeg
```

# 5. さいごに
このフローではまだ、`スコープ`や`Apply to each`などの構造化したフローを表現することができません。
各アクションの`type`を確認して構造化したアクションごとの処理を定義することで可能ですが、今回は長くなったのでここまでにしておきます。
（Power Automate で複雑なフローを無理やり画像化するよりも、新しいデザイナーで単純にスクショ取る方がきれいに見えそうですし）

フローをフローチャートにすることにはあまり意味がありませんが、Mermaid記法を使って様々な図を生成できるというのは、Power Automate と連携して**何かまだ面白いことができるのでは？** と思い、執筆してみました。

皆さんのアイデアを待っています！