Watson Knowledg Catalog for CP4D ~API色々~

# はじめに
Watson Knowledge Catalog for CP4D（WKC for CP4D）のAPIドキュメントに記載されている情報が少なく、APIで色々実施しようとすると苦労するので、今まで調べた内容を備忘録としてメモっておきます。
調べたAPIが増えたら、適宜この記事を更新しようと思います。

あくまでも例です。より良い方法が他にもあるかもしれません。そして、コーディング初心者なのでその点ご了承ください。

**試した環境 : WKC for CP4D 4.0.5**

# 参考資料
全体的なAPI Reference
[Watson Data API](https://cloud.ibm.com/apidocs/watson-data-api#introduction)

アセット（資産）に特化したAPI Reference
実は、こっちの方が詳しい。
[Catalog and Asset Management Service](https://api.dataplatform.cloud.ibm.com/v2/cams/explorer/#/)

# 認証情報の取得
何を始めるのにも、まずは認証Tokenを取得する必要があります。
`POST /icp4d-api/v1/authorize` を使用します。

```python
cp4d_url = "https://cp4dのホスト名"
cp4d_port = "<ポート番号>"
cp4d_username = "admin"
cp4d_password = "<adminのパスワード>"

headers = {
    'cache-control': 'no-cache',
    'content-type': 'application/json',
}

data = '{"username":"' + cp4d_username + '","password":"' + cp4d_password + '"}'

response = requests.post(cp4d_url + ':' + cp4d_port + '/icp4d-api/v1/authorize', headers=headers, data=data, verify=False)

```
こうすると `token`  が取得できます。
```python
response_data=json.loads(response.text)
cp4d_token = response_data['token']
```

その後は、以下のようにし認証情報を `headers` に指定して各種APIを実行します。
```python
cp4d_auth ='Bearer ' + cp4d_token
headers = {
    'Authorization': cp4d_auth
}
```

# 各種IDの取得方法
## カタログID（catalog_id）
カタログに登録されているデータ資産をAPIで更新したい場合などは、カタログIDが必要となります。
CP4Dに作成されているカタログをリストするAPI `GET /v2/catalogs` を使用します。
```python
catalog_response = requests.get(cp4d_url + ':' + cp4d_port + '/v2/catalogs/', headers=headers, verify=False)
```
すると、こんな結果が出力されます。`guid` が`TestCatalog01`の `catalog_id` です。
```json
{
	"catalogs": [
		{
			"metadata": {
				"guid": "69fe7c4e-87ef-4e04-8ccd-38ac4150ec74",
				"url": "/v2/catalogs/69fe7c4e-87ef-4e04-8ccd-38ac4150ec74",
				"creator_id": "1000330999",
				"create_time": "2022-02-22T08:12:57Z"
			},
			"entity": {
				"name": "TestCatalog01",
				"description": "これはテストのカタログです",
:
:
```

## 資産ID（asset_id）
特定のカタログに登録されている資産を更新したい場合は、その資産IDが必要となります。
カタログの中を全検索すると個々の資産IDを取得することができます。
`GET /v2/asset_types/{type_name}/search` を使用します。

`type_name`は取得したい資産の種類によって適切な値を指定します。
例えば、データ資産であれば `data_asset` と指定します。
指定できる資産の種類については以下を参照してください。

https://cloud.ibm.com/apidocs/watson-data-api#Section_Assets__Overview_and_Terminology__Asset_Type

```python
catalog_id = '<上記で取得したカタログID>'
cp4d_auth ='Bearer ' + cp4d_token
headers = {
    'Authorization': cp4d_auth,
    'cache-control': 'no-cache',
    'content-type': 'application/json'
}

# body部分 *:* とすることで全検索になる。
data = '{"query": "*:*", "limit": 200}'

catalog_properties = requests.post(cp4d_url + ':' + cp4d_port + '/v2/asset_types/data_asset/search?catalog_id=' + catalog_id, headers=headers, data=data.encode('utf-8'), verify=False)

```
各アセットの詳細とともに `asset_id` が出力されます。

## 資産名を変更したい
例えば、Db2の表をカタログに登録した後に、カタログ上の資産名を変更したいとなった場合の例をご紹介します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148302/ebbad00c-ea46-e04d-5c5e-dbd4d60d78dd.png)

`PATCH /v2/assets/{asset_id}`を使用します。

```python
catalog_id = '<カタログID>'
asset_id = '<更新対象の資産ID>'

cp4d_auth ='Bearer ' + cp4d_token
headers = {
    'Authorization': cp4d_auth,
    'cache-control': 'no-cache',
    'content-type': 'application/json'
}

# RFC 6902に準拠
data = '[{"op":"add","path":"/metadata/name","value":"T2の名前をAPIで更新してみた"}]'

assetname_properties = requests.patch(cp4d_url + ':' + cp4d_port + '/v2/assets/' + asset_id + '?catalog_id=' + catalog_id, headers=headers, data=data.encode('utf-8'), verify=False)

```
ポイントは以下の部分です。
`data = '[{"op":"add","path":"/metadata/name","value":"T2の名前をAPIで更新してみた"}]'`
詳しくは存じ上げませんが、RFC 6902 という **JavaScript Object Notation (JSON) Patch** に準拠する必要があるそうです。
* 正式なドキュメントは[こちら](https://www.rfc-editor.org/rfc/rfc6902)。
* 非公式ですが、日本語訳もありました。[こちら](https://triple-underscore.github.io/RFC6902-ja.html)。

`path` に何を指定するかはこちらのAPI Referenceを見ると分かります。
https://api.dataplatform.cloud.ibm.com/v2/cams/explorer/#!/Assets/update


## カラムに説明を追加したい
続いて、データ資産のカラム（列）に対して説明を追加したい場合の例をご紹介します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148302/7d3779bd-fd0d-df2f-41a8-c4f54e05a0e4.png)

`PATCH /v2/assets/{asset_id}/attributes/{attribute_key}` を使います。
`attribute_key` は `column_info`です。

```python
catalog_id = '<カタログID>'
asset_id = '<更新対象の資産ID>'
attribute_key = 'column_info'

cp4d_auth ='Bearer ' + cp4d_token
headers = {
    'Authorization': cp4d_auth,
    'cache-control': 'no-cache',
    'content-type': 'application/json'
}

# RFC 6902に準拠
data = '[{"op":"add","path":"/C1/column_description","value":"C1の説明文をAPIで更新してみた"}]'

column_properties = requests.patch(cp4d_url + ':' + cp4d_port + '/v2/assets/' + asset_id + '/attributes/' + attribute_key + '?catalog_id=' + catalog_id, headers=headers, data=data.encode('utf-8'), verify=False)

```

ここでもポイントは以下の部分です。
`data = '[{"op":"add","path":"/C1/column_description","value":"C1の説明文をAPIで更新してみた"}]'`

[/v2/assets/{asset_id}/attrivutes/{attribute_key}](https://api.dataplatform.cloud.ibm.com/v2/cams/explorer/#!/Asset_Attributes/updateAssetAttributeNewV2) で資産の属性を変更する場合、`attribute_key` や `path` や `value` に何を指定すれば良いか分からないことがあります。
そんな時は、サンプル資産を手動でWKCに登録して、必要な属性を付与した後に、`GET /v2/assets/{asset_id}/attributes` でアセット属性を取得します。
JSONフォーマットで詳細情報が出力されるので、それを参考にそれぞれの値を指定すると良さそうです。


## 資産にビジネス用語を追加したい
これも、使うのは `PATCH /v2/assets/{asset_id}/attributes/{attribute_key}` です。
`attribute_key` は `asset_terms`。

```python
catalog_id = '<カタログID>'
asset_id = '<更新対象の資産ID>'
attribute_key = 'asset_terms'

cp4d_auth ='Bearer ' + cp4d_token
headers = {
    'Authorization': cp4d_auth,
    'cache-control': 'no-cache',
    'content-type': 'application/json'
}

# RFC 6902に準拠
data = '[{"op":"add","path":"/list","value":[{"term_display_name": "ビジネス用語01","term_id": "<ビジネス用語のID>"}]}]'

bterm_properties = requests.patch(cp4d_url + ':' + cp4d_port + '/v2/assets/' + asset_id + '/attributes/' + attribute_key + '?catalog_id=' + catalog_id, headers=headers, data=data.encode('utf-8'), verify=False)

```

`ビジネス用語01` のIDは `GET /v3/glossary_terms` で取得した `global_id` を指定します。
* 環境に沢山のビジネス用語が登録されている場合、`limit=300` などパラメータを指定しないと全て出力されないためご注意ください。


## 資産に分類を紐づけたい
これも、使うのは `PATCH /v2/assets/{asset_id}/attributes/{attribute_key}` です。
`attribute_key` はまさかの `data_profile`。

やはり、事前にサンプル資産を手動でWKCに登録して、必要な属性を付与した後に、`GET /v2/assets/{asset_id}/attributes` でアセット属性を取得し、出力されたJSONを参考にするしかなさそうですね。

```python
catalog_id = '<カタログID>'
asset_id = '<更新対象の資産ID>'
attribute_key = 'data_profile'

cp4d_auth ='Bearer ' + cp4d_token
headers = {
    'Authorization': cp4d_auth,
    'cache-control': 'no-cache',
    'content-type': 'application/json'
}

#  GET /v2/assets/{asset_id}/attributes の出力結果で分類情報が記載されていた形式はこんな感じ
# 'data_classification_manual': [{'id': '<分類ID>',
#  'name': 'テストカテゴリ',
#  'global_id': '<分類のGlobalID>'}]

# 上記記述に従って、path と value を書く！
data = '[{"op":"add","path":"/data_classification_manual","value":[{"id": "<分類ID>","name":"テストカテゴリ","global_id":"<分類のGlobalID>"}]}]'

classifications_properties = requests.patch(cp4d_url + ':' + cp4d_port + '/v2/assets/' + asset_id + '/attributes/' + attribute_key + '?catalog_id=' + catalog_id, headers=headers, data=data.encode('utf-8'), verify=False)

```

`分類ID` と `分類のGlobalID` は `GET /v3/classifications` で取得できます。
* `artifact_id` = `分類ID`
* `global_id` = `分類のGlobalID`


## 資産にタグをつけたい
`PATCH /v2/assets/{asset_id}` を使います。
以下の例では、`apitag01` と `apitag02` という2つのタグをデータ資産につけてます。

```python
catalog_id = '<カタログID>'
asset_id = '<更新対象の資産ID>'

cp4d_auth ='Bearer ' + cp4d_token
headers = {
    'Authorization': cp4d_auth,
    'cache-control': 'no-cache',
    'content-type': 'application/json'
}

# RFC 6902に準拠
data = '[{"op":"add","path":"/metadata/tags","value":["apitag01","apitag02"]}]'

assetname_properties = requests.patch(cp4d_url + ':' + cp4d_port + '/v2/assets/' + asset_id + '?catalog_id=' + catalog_id, headers=headers, data=data.encode('utf-8'), verify=False)

```

ポイントは
`data = '[{"op":"add","path":"/metadata/tags","value":["apitag01","apitag02"]}]'`

* `path` については[こちら](https://api.dataplatform.cloud.ibm.com/v2/cams/explorer/#!/Assets/update)を参考
* `value` についてはサンプル資産を登録した結果を参考にしました

## プラットフォーム接続に接続を作成したい
`POST /v2/connections` を使います。API Referenceは[こちら](https://cloud.ibm.com/apidocs/watson-data-api#connections)。
API Referenceは情報が少なく試行錯誤しました。。。。

以下は、DBの接続を作る例です。
```python
cp4d_auth ='Bearer ' + cp4d_token
headers = {
    'Authorization': cp4d_auth,
    'cache-control': 'no-cache',
    'content-type': 'application/json'
}

datasouce_type_id = '<データソース・タイプID>'
dbname = '<任意の接続名>'
hostname = '<DBのホスト名>'
dbport = '<ポート番号>'
database = '<DB名>'
dbusername ='<ユーザー名>'
dbpasssword ='<パスワード>'

"""
 Bodyはこんな感じ
 Sample body
 {
    "datasource_type": "cfdc**************7",
 	"name":"My-Sample-Connection",
    "properties": {
 	   "host":"dbhost.com",
 	   "port":"50001",
 	   "database":"MYBLUDDB",
       "username": "myusername",
       "password": "mypassword"
     }
 }
"""

data_body = '{"datasource_type":"' + datasouce_type_id + '","name":"' + dbname +  '","origin_country":"us' + '","properties":{ "host": "' + hostname + '","port":"' + dbport +'","database":"'+ database + '","password":"' + dbpasssword + '","username":"' + dbusername + '","ssl": "true"}}' 

catalog_id = '<Platform assets catalogのID>'
create_connection_response = requests.post(cp4d_url + ':' + cp4d_port + '/v2/connections?catalog_id=' + catalog_id, headers=headers, data=data_body.encode('utf-8'), verify=False)

```
DBの接続情報は、それぞれのDBから取得してください。
ポイントは、`/v2/connections?catalog_id=` で指定する *カタログID* です。
プラットフォーム接続に登録されている接続情報は、WKCで最初から存在する **Platform assets catalog** にも登録されます。
プラットフォーム接続事態はIDを持っていないので、**Platform assets catalog** のIDを指定します。

`datasouce_type_id = '<データソース・タイプID>'` は、`GET /v2/datasource_types` で取得します。
作成したいデータソースのタイプに応じてIDが存在するため、その `asset_id` を指定します。



## プラットフォーム接続に汎用JDBC接続を作成したい
これがちょっと厄介。
基本的には、上記で紹介した`POST /v2/connections` を使います。
接続情報がポイントとなるのでBody部分が違うだけです。

```json:Db2の汎用JDBC接続をする際のBody例
{
 "datasource_type": "<汎用JDBCのID>",
 "name": "<接続名>",
 "origin_country": "us",
 "properties": {
  "jdbc_properties": "sslConnection=true>",
  "jar_uris": "/v2/asset_files/dbdrivers/db2jcc4.jar?account_id=999&root=true&signature=vVT*****3D",
  "jdbc_driver": "com.ibm.db2.jcc.DB2Driver",
  "ssl": "true",
  "jdbc_url": "jdbc:db2://5***ain.cloud:32733/bludb",
  "username": "<ユーザー名>",
  "password": "<パスワード>"
 },
 "flags": [
  "personal_credentials"
 ]
}
```

ポイントとなる設定内容は以下の通りです。

| key | value |
|:--|:--|
| datasource_type  | 汎用JDBCのデータソース・タイプID  |
| jdbc_properties | JDBCプロパティに指定する設定  |
| jar_uris  | これが厄介。<br>どうやってAPIでこの情報を取得するのかいまだに不明。<br>なので、サンプルの接続を作り GET /v2/assets/{asset_id} をから得られた jar_uris をそのまま指定しました。  |
| jdbc_driver  | JDBCドライバー名  |
| jdbc_url  | JDBC接続URL  |
| username  | DB接続ユーザー名  |
| password  | パスワード  |
| flags  | 接続を個人用にする場合に必要。共有の場合は不要。  |


## 今回はここまで


# お断り
このサイトの掲載内容は私自身の見解であり、必ずしも所属会社の立場、戦略、意見を代表するものではありません。 記事は執筆時点の情報を元に書いているため、必ずしも最新情報であるとはかぎりません。 記事の内容の正確性には責任を負いません。自己責任で実行してください。