KintoneのREST APIをpython3で使ってワークフローを自動化する

# Kintone とは

Cybozu社の提供するクラウドサービスです。
Webデータベースを簡単に作成できます。
作成されたデータベースのことをKintoneでは「アプリ」と呼びます。

コミュニティ活動が活発で、頻繁に勉強会が開催され、ハッカソンでの技術提供も見かけます

## kintone devCamp
サイボウズ主催の勉強会

https://kintonedevcamp.connpass.com/

## kintone Café
有志の勉強会

https://www.kintonecafe.com/

## Webデータベースの使い分け

Webデータベースのクラウドサービスは他にもいくつかあり、例えば「Hexabase」があります。

https://www.hexabase.com/product/

Kintoneとの大きな違いはフロントエンド機能のサポート範囲です。
Kintoneはアプリ内で完結させるため、WebデータベースのGUI機能が充実しています。
HexabaseはSPA等のフロントエンドを別に用意することが前提で、各種フレームワークのSDKが充実しています。

簡易なWebデータベースで十分ならKintone、より高度なフロントエンドが必要ならHexabase、という使い分けになります。

# 環境準備

kintoneは30日間の無料お試しのほかに、[開発者ライセンス](https://cybozudev.zendesk.com/hc/ja/articles/200720464-kintone-%E9%96%8B%E7%99%BA%E8%80%85%E3%83%A9%E3%82%A4%E3%82%BB%E3%83%B3%E3%82%B9-%E9%96%8B%E7%99%BA%E7%92%B0%E5%A2%83-)があります。

開発者ライセンスの注意事項は公式ドキュメントの通りです。
エンジニアがちょっとKintone触ってみたい、という時には開発者ライセンスで十分です。

# アプリを作る

開発者ライセンスが取得できて、開発者環境のログインURL/ID/PASSが通知されたら、さっそくログインしてアプリを作ってみましょう。

アプリの作成方法は公式ドキュメントに乗っているので割愛します。

https://jp.cybozu.help/k/ja/user/create_app/tutorial.html

レコード番号、ステータス、作業内容のフィールドを作りました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628523/6481c67c-3bf6-1cf0-8380-19108267b084.png)

ステータス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628523/c0e986ab-0aaa-0aa1-4d4e-45a4c38912f5.png)

作業内容
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628523/e82a254e-ce38-ec0a-9569-d7a676ddc43b.png)


フィールドコードはデフォルトでは日本語が書かれてますが、後々のAPI利用で面倒になります。
変数名を付ける感じで英語にしておきましょう。
好みの問題ですが、ここでは大文字のスネークケースにします。

##  補足 プロセス管理

今回はステータスというフィールドを用意しました。
もし、ワークフローツールとしてステータスを変更できる権限を制限したり、
誰がいつ変更したか等の証跡を残したい場合はプロセス管理を使う方が適切です。

https://jp.cybozu.help/k/ja/user/app_settings/process/set_process.html

しかしREST APIでAPIトークンを用いたリクエストはAdministratorユーザとして実行されます。

https://cybozu.dev/ja/kintone/docs/rest-api/overview/authentication/#api-token

プロセス管理では、プロセスを変更できるユーザを作業者として指定する必要があります。
そのため、Administratorユーザを利用ユーザとして扱うような、非推奨の設定が必要になります。
また本来の「誰が」という情報が欠落してしまいます。

認証にID/PASSを用いたリクエストでこの問題は解決できます
しかし属人的な情報を作業の自動化に用いるのは、アカウントの共有と同じく「誰が」という情報を曖昧にします。
チームとして使用する場合はプロセス管理の必要性を見直す方が良いでしょう。
個人の作業を自動化したい場合はこの限りではありません。

実際のアプリではプロセス管理を使うか、フィールドで十分なのか、慎重に設計する必要があります。

# REST APIを使う
使い方や種類は公式ドキュメントがあるので割愛します。

https://cybozu.dev/ja/kintone/docs/rest-api/list/

自動化で必要になりそうな、レコードのGET/POST/PUTを使ってみます。
まずはGETから試します。

## 事前準備
GETで取得するレコードを1つ登録しておきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628523/01588522-7b2a-7c47-4141-71a4ec9c33a8.png)

APIトークンを用いてREST APIを使うには、APIトークンを作成する必要があります。
このタイミングで作成しておきましょう。

https://jp.cybozu.help/k/ja/user/app_settings/api_token.html

アクセス権は全てに付与しておきます。
実際の運用では必要最低限に絞り、スクリプトに応じてAPIトークンも使い分けましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628523/fc97f098-02b7-738a-bdf2-ee65e7caf001.png)

## GET
レコードの一覧を取得するスクリプトを実行してみましょう。
```python:get.py
import requests
import json

url = "https://your-domain.cybozu.com/k/v1/records.json"
headers = {"X-Cybozu-API-Token": "your-api-token", "Content-Type" : "application/json"}
data = {"app": "your-app-id"}

response = requests.get(url, json=data, headers=headers)

print(json.dumps(response.json(), indent=2, ensure_ascii=False))
```
```json:reponse
{
  "records": [
    {
      "STATUS": {
        "type": "DROP_DOWN",
        "value": "未着手"
      },
      "レコード番号": {
        "type": "RECORD_NUMBER",
        "value": "1"
      },
      "更新者": {
        "type": "MODIFIER",
        "value": {
          "code": "your-code",
          "name": "your-name"
        }
      },
      "作成者": {
        "type": "CREATOR",
        "value": {
          "code": "your-code",
          "name": "your-name"
        }
      },
      "WORK": {
        "type": "SINGLE_LINE_TEXT",
        "value": "GETする"
      },
      "$revision": {
        "type": "__REVISION__",
        "value": "1"
      },
      "更新日時": {
        "type": "UPDATED_TIME",
        "value": "2023-03-03T06:23:00Z"
      },
      "作成日時": {
        "type": "CREATED_TIME",
        "value": "2023-03-03T06:23:00Z"
      },
      "$id": {
        "type": "__ID__",
        "value": "1"
      }
    }
  ],
  "totalCount": null
}
```
レコードの一覧が取得できました。

アプリでは3つのフィールドしか設定しませんでしたが、レコード情報を保存しているメタフィールドがあります。
これらのフィールドはAPIでの扱いが他のフィールドより制限されていることがあります。
POST/PUTする場合は注意しましょう。

https://cybozu.dev/ja/kintone/docs/overview/field-types/

## POST
次にレコードを作成してみましょう。
値の指定を省略したフィールドは初期値が入ります。

必須フィールドが省略された場合はエラーになります。
自動化を見据えてアプリを作成する場合は必須属性を増やさないことも大切です。

エンドポイントが`/record.json`に変わっていることに注意してください。
```python:post.py
import requests
import json

url = "https://your-domain.cybozu.com/k/v1/record.json"
headers = {"X-Cybozu-API-Token": "your-api-token", "Content-Type" : "application/json"}
data = {"app": "your-app-id"}

response = requests.get(url, json=data, headers=headers)

print(json.dumps(response.json(), indent=2, ensure_ascii=False))
```
```json:response
{
  "id": "2",
  "revision": "1"
}
```
レコード作成が成功すると、レコードIDとリビジョンが返答されます。
内容を確認したい場合は再度GETするかGUIから確認しましょう。
GETではレコード単位での取得も可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628523/3eea3834-44be-87ca-2a7e-ae6f6909b467.png)
ステータスが未着手、作業内容が空のレコードが作成されました。

## PUT
最後にレコードを編集してみましょう。
値の指定を省略したフィールドは変更されません。

ただしフィールドがサブテーブルの場合は注意が必要です。
データはサブテーブル単位でのみ変更可能で、変更行だけ記載すると他の行が消えてしまいます。

```python:put.py
import requests
import json

url = "https://your-domain.cybozu.com/k/v1/record.json"
headers = {"X-Cybozu-API-Token": "your-api-token", "Content-Type" : "application/json"}
data = {
    "app": "your-app-id",
    "id": "2",
    "record": {
        "STATUS": {
            "value": "進行中",
        },
    },
}


print(json.dumps(response.json(), indent=2, ensure_ascii=False))
```
```json:response
{
  "revision": "2"
}
```
更新に成功すると、最新のリビジョンが返答されました。
GUIで確認するとステータスが更新されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628523/61d6efb3-796e-7af0-6790-6ab0e5528ccc.png)

## 補足 APIトークンの実行ユーザ
先ほど、APIトークンを用いた場合Administratorユーザで実行されると書きました。
どういうことか実際に確認してみましょう。

先ほどの`get.py`を実行しレスポンスを見てみます。
全て載せると長くなるので一部省略しています。
```json:response
{
  "records": [
    {
      ...略
      "レコード番号": {
        "type": "RECORD_NUMBER",
        "value": "2"
      },
      "更新者": {
        "type": "MODIFIER",
        "value": {
          "code": "Administrator",
          "name": "Administrator"
        }
      },
      "作成者": {
        "type": "CREATOR",
        "value": {
          "code": "Administrator",
          "name": "Administrator"
        }
      },
      ...略
    },
    {
      ...略
      "レコード番号": {
        "type": "RECORD_NUMBER",
        "value": "1"
      },
      "更新者": {
        "type": "MODIFIER",
        "value": {
          "code": "your-code",
          "name": "your-name"
        }
      },
      "作成者": {
        "type": "CREATOR",
        "value": {
          "code": "your-code",
          "name": "your-name"
        }
      },
      ...略
    }
  ],
  "totalCount": null
}
```
APIでPOST/PUTしたレコードは作成者・更新者がAdministratorになっています。
もしアプリが作成者・更新者を使ってプロセス管理等を機能させる場合、注意しましょう。

# まとめ
KintoneのREST APIを使ってワークフローを自動化するための方法を紹介しました。
公式ドキュメントが充実しているので、もっと知りたい方は是非そちらを参照してください。

この記事が業務自動化のためになれば幸いです。