Node-REDでOpenMetadataにアクセスしてみた

[Node-RED](https://nodered.org/ )&reg;はローコードツールのひとつで、フローエディタという設定機能上でプログラミングをすることなく（必要であればコーディングも可能）アプリケーションを開発できるソフトウェアです。  
今回、Node-REDで[OpenMetadata](https://docs.open-metadata.org/)というデータベースなどのメタデータを管理するデータカタログツールにアクセスしてみました。  

# データの準備
[こちらの記事](https://qiita.com/t-kataym/items/fead7a0bbbcb41f337cf)を参考にOpenMetadataにメタデータを取り込んでおきました。

# Node-REDの準備
Node-REDのバージョンは3.1.8を使用しました。  
追加ノードとして、OpenMetadataにアクセスするノードである [@pgspider/node-red-openmetadata](https://flows.nodered.org/node/@pgspider/node-red-openmetadata) (1.0.1) をインストールしました。

右上のメニューから、「パレットの管理」→「ノードを追加」をクリックし、上記のノードをインストールできます。

![add_node](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581362/5f68896a-24c5-88c6-70b8-41c3dc901e02.png)

以下の図のように、ストレージのところにopenmetadataという薄紫色のノードがあればOKです。

![om-node](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581362/ea3bd6c0-4284-9b55-3193-47d88b7a1a4c.png)

# フローの作成
フローのサンプルは[本記事の末尾](#appendix)に記載しておきました。

今回は、Injectionノードを使って実行をキックし、OpenMetadataから取得したデータはデバッグウィンドウに出力させて確認します。

以下の図のように「Inject」ノード、「openmetadata」ノード、「debug」ノードを配置します。

![flow](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581362/0dd7eb9e-53da-1694-f370-cee450b74176.png)

## openmetadataノードの設定
openmetadataノードをダブルクリックして編集画面を開きます。  
サーバのところのペンマークをクリックして、OpenMetadataへの接続情報を入力します。  
エンドポイントには、「http://ホスト名:ポート番号/api/v1/」を指定します。  

![omnode-config1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581362/8c39c5c0-0d09-a551-46f6-6852c8858d3b.png)

認証を通すために、ユーザ名およびパスワード、あるいはトークンの入力が必要です。

![omnode-config2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581362/e072506e-32f4-ae48-c654-3c89c27aded0.png)

入力後「追加」をクリックします。  
最後に、OpenMetadataから取得したいメタデータをプルダウンメニューから選びます。
今回はテーブル情報の一覧を取得するために、「テーブルをリストする」を設定しました。  
取得したいメタデータによって、設定パラメータが異なります。  
必須項目を確認したいときは「オプションのパラメータを表示する」のチェックを外します。

![omnode-config3](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581362/21e7538e-8280-438c-26f1-1fc1311319cc.png)

これで設定は完了です。

# 実行
injectノードの左のボタンをクリックして実行します。  

以下の図のようにデバッグウィンドウにメタデータが出力されるはずです。

![result1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581362/ce714f72-7236-c5a8-0f82-6aaedde57313.png)

# 補足
オプションパラメータの指定において注意点があります。

「テーブルをリストする」などではデータベースやスキーマを指定して、取得対象の範囲を指定することができます。  
オプション名としては"database"や"databaseSchema"です。  
databaseフィールドの設定値は、単純にデータベース上のデータベース名を指定するのではなく、"OpenMetadata上のサービス名.データベース名"を指定する必要があります。  
同様に、databaseSchemaフィールドの設定値は、単純にデータベース上のスキーマ名を指定するのではなく、"OpenMetadata上のサービス名.データベース名.スキーマ名"を指定する必要があります（下図）。  
（databaseSchemaフィールドを設定した場合、databaseの設定値を含んでいるため、databaseフィールドを設定する必要はありません）

![option](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581362/f4b89ce3-6754-56d6-2257-ee54665bbbf9.png)

# おわりに
Node-REDでOpenMetadataにアクセスする方法を説明しました。  
node-red-openmetadataノードをインストールするだけで簡単にメタデータを取得可能になります。
このノードを使えば、OpenMetadataのメタデータを使ったアプリケーションのPoCがNode-REDで簡単に作れます。  

補足に書いたように、databaseやdatabaseSchemaのオプション指定では、そのリソースの上位の階層も含めて値を指定しないといけません。これはOpenMetadata APIの仕様に起因しています。  
つまり、このオプションを使うことで、以下の条件で検索対象を絞ることができます。
* 特定のサービス、特定のデータベース、任意のスキーマ（databaseオプションを使用）
* 特定のサービス、特定のデータベース、特定のスキーマ（databaseSchemaオプションを使用）


# 参考文献
* Node-RED: https://nodered.org/  
* OpenMetadata: https://docs.open-metadata.org/  
* @pgspider/node-red-openmetadata: https://flows.nodered.org/node/@pgspider/node-red-openmetadata  

# Appendix

<details>
<summary>フロー</summary>

```json
[
    {
        "id": "f6f2187d.f17ca8",
        "type": "tab",
        "label": "Flow 1",
        "disabled": false,
        "info": ""
    },
    {
        "id": "3cc11d24.ff01a2",
        "type": "comment",
        "z": "f6f2187d.f17ca8",
        "name": "WARNING: please check you have started this container with a volume that is mounted to /data\\n otherwise any flow changes are lost when you redeploy or upgrade the container\\n (e.g. upgrade to a more recent node-red docker image).\\n  If you are using named volumes you can ignore this warning.\\n Double click or see info side panel to learn how to start Node-RED in Docker to save your work",
        "info": "\nTo start docker with a bind mount volume (-v option), for example:\n\n```\ndocker run -it -p 1880:1880 -v /home/user/node_red_data:/data --name mynodered nodered/node-red\n```\n\nwhere `/home/user/node_red_data` is a directory on your host machine where you want to store your flows.\n\nIf you do not do this then you can experiment and redploy flows, but if you restart or upgrade the container the flows will be disconnected and lost. \n\nThey will still exist in a hidden data volume, which can be recovered using standard docker techniques, but that is much more complex than just starting with a named volume as described above.",
        "x": 350,
        "y": 80,
        "wires": []
    },
    {
        "id": "c0216df0a631cb48",
        "type": "openmetadata",
        "z": "f6f2187d.f17ca8",
        "nodename": "",
        "openMetadataConfig": "430ccc52e19afb9e",
        "resource": "list_tables",
        "showOptParams": true,
        "q": "",
        "name": "",
        "id_param": "",
        "fqn": "",
        "startTs": "",
        "endTs": "",
        "index": "",
        "deleted": "false",
        "from": "",
        "size": "",
        "sort_field": "",
        "sort_order": "",
        "track_total_hits": "false",
        "query_filter": "",
        "post_filter": "",
        "fetch_source": "true",
        "include_source_fields": "",
        "parent": "",
        "fields": "",
        "domain": "",
        "service": "",
        "database": "",
        "disabled": "",
        "databaseSchema": "",
        "includeEmptyTestSuite": "true",
        "limit": "",
        "before": "",
        "after": "",
        "include": "",
        "qFieldType": "str",
        "nameFieldType": "str",
        "id_paramFieldType": "str",
        "fqnFieldType": "str",
        "startTsFieldType": "num",
        "endTsFieldType": "num",
        "indexFieldType": "str",
        "deletedFieldType": "bool",
        "fromFieldType": "num",
        "sizeFieldType": "num",
        "sort_fieldFieldType": "str",
        "sort_orderFieldType": "str",
        "track_total_hitsFieldType": "bool",
        "query_filterFieldType": "str",
        "post_filterFieldType": "str",
        "fetch_sourceFieldType": "bool",
        "include_source_fieldsFieldType": "str",
        "parentFieldType": "str",
        "fieldsFieldType": "str",
        "domainFieldType": "str",
        "serviceFieldType": "str",
        "databaseFieldType": "str",
        "disabledFieldType": "str",
        "databaseSchemaFieldType": "str",
        "includeEmptyTestSuiteFieldType": "bool",
        "limitFieldType": "num",
        "beforeFieldType": "str",
        "afterFieldType": "str",
        "includeFieldType": "str",
        "x": 400,
        "y": 280,
        "wires": [
            [
                "c1c69f1ba506b0f3"
            ]
        ]
    },
    {
        "id": "5422fbff7b952cc4",
        "type": "inject",
        "z": "f6f2187d.f17ca8",
        "name": "inject",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 170,
        "y": 280,
        "wires": [
            [
                "c0216df0a631cb48"
            ]
        ]
    },
    {
        "id": "c1c69f1ba506b0f3",
        "type": "debug",
        "z": "f6f2187d.f17ca8",
        "name": "debug 1",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 640,
        "y": 280,
        "wires": []
    },
    {
        "id": "430ccc52e19afb9e",
        "type": "openMetadataConfig",
        "name": "",
        "endpoint": "http://localhost:8585/api/v1/",
        "endpointFieldType": "str",
        "user": "admin",
        "userFieldType": "str",
        "password": "admin",
        "passwordFieldType": "str",
        "tokenInput": "",
        "tokenInputFieldType": "str",
        "token": "",
        "proxy": "",
        "proxyFieldType": "str",
        "noproxy": false
    }
]
```
</details>

---  
社名・商品名・サービス名などは、それぞれ各社が商標として使用している場合があります。​