デプロイ API を使ってコマンドラインでアプリをコピーしてみよう

著者名: KADOYA Ryo (External link)

目次

はじめに

今回は、コマンドライン HTTP クライアントの curl を使って、既存のアプリを別の kintone にコピーしてみたいと思います。
デプロイ API でアプリをコピーするときのハマりどころについて重点的に説明します。

アプリ情報を取得・設定する API やデプロイ API について、次の API ドキュメントを参考してください。
アプリの設定を操作する kintone REST API

下準備

コマンドラインで curl を利用します。
curl は Linux や MacOS X で標準搭載されています。
Windows でも cygwin などを使って利用できます。

curl (External link)

curlの基本的な使い方

curl で kintone にアクセスするうえでの基本的なオプションは以下です。

1
curl -X <メソッド> -H <ヘッダー情報> -d <パラメータ>

共通のパラメーターを事前に定義しておきます。
以下をターミナルで実行します。
user:password および subdomain の部分は適宜置き換えてください。

1
2
3
4
BASE64=`echo -n 'user:password' | openssl base64`
AUTH="X-Cybozu-Authorization: ${BASE64}"
JSON="Content-Type: application/json"
HOST=“https://subdomain.cybozu.com/k"

以下で、curl で kintone から情報を取得してみました。

1
2
3
curl -X GET -H "$AUTH" -H "$JSON" -d '{id:160}'  ${HOST}/v1/app.json

{"appId":"160","code":"","createdAt":"2015-08-21T10:42:16.000Z","creator":{"code":"Ita","name":"ita"},"description":"日々の業務内容、報告事項、所感などを記載していくアプリです。\u003Cdiv\u003E記録を行うだけでなく、あとからの振り返りやメンバー間のコミュニケーションにも活用できます。\u003C/div\u003E","modifiedAt":"2015-08-21T10:43:25.000Z","modifier":{"code":"Ita","name":"ita"},"name":"日報","spaceId":null,"threadId":null}

BASIC 認証を使用する場合は、以下のように BASIC 認証用のヘッダーを追加してください。

1
2
BASIC="basic\_user:basic\_password"
curl -X GET --user "$BASIC" -H "$AUTH" -H "$JSON" -d '{id:160}'  ${HOST}/v1/app.json

この日報アプリを、API を使ってコピーしてみることにします。

アプリの作成

動作テスト環境にアプリを作成する API を使用してアプリを作成します。

1
2
3
curl -X POST -H "$AUTH" -H "$JSON" -d '{name:"コピーされたアプリ"}'  ${HOST}/v1/preview/app.json

{"app":"162","revision":"2"}

アプリ ID: 162 のアプリが作成されました。
まだ運用環境には適用されていないのでトップページには表示されませんが、以下の URL から管理画面にアクセスできます。
https://subdomain.cybozu.com/k/admin/app/flow?app=162

一般設定情報のコピー

コピー元アプリの設定情報を取得する

それではまず、コピー元のアプリから一般設定情報を取得します。

1
curl -X GET -H "$AUTH" -H "$JSON" -d '{app:160}'  ${HOST}/v1/preview/app/settings.json > settings.json

取得した設定情報を編集する

ここで取得した一般設定情報を、新しく作成したアプリに適用しますが、取得した json をそのまま適用できません。
app パラメーターを付与し、revision パラメーターを除外する必要があります。

取得した settings.json の中身は次のとおりです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
    "description":"日々の業務内容、報告事項、所感などを記載していくアプリです。\u003Cdiv\u003E記録を行うだけでなく、あとからの振り返りやメンバー間のコミュニケーションにも活用できます。\u003C/div\u003E",
    "icon":{
        "key":"APP15",
        "type":"PRESET"
    },
    "name":"日報",
    "revision":"7",
    "theme":"CLIPBOARD"
}

app パラメーターを付与し、revision パラメーターを除外します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
    "app":162,
    "description":"日々の業務内容、報告事項、所感などを記載していくアプリです。\u003Cdiv\u003E記録を行うだけでなく、あとからの振り返りやメンバー間のコミュニケーションにも活用できます。\u003C/div\u003E",
    "icon":{
        "key":"APP15",
        "type":"PRESET"
    },
    "name":"日報",
    "theme":"CLIPBOARD"
}

設定情報を適用する

修正した json を PUT メソッドで送信します。
curl では -d オプションに @ をつけてファイルを指定できます。

1
2
3
curl -X PUT -H "$AUTH" -H "$JSON" -d '@settings.json' ${HOST}/v1/preview/app/settings.json

{"revision":"3"}

フォーム情報のコピー

フォーム情報を取得する

同様に、コピー元のアプリから情報を取得します。

1
curl -X GET -H "$AUTH" -H "$JSON" -d '{app:160}' ${HOST}/v1/preview/app/form/fields.json > fields.json

取得したフォーム情報を編集する

ここで取得したデータも、やはりそのままでは送信できません。
app/revision パラメーターの処理に加えて、ビルトインフィールドの問題があるためです。

ビルトインフィールドは、アプリの作成時に自動的に追加されるため、フィールドの追加 API での追加はできません。
したがってこれらのフィールドはアプリの新規追加時でも、変更 API を使用する必要があります。

ビルトインフィールドの一覧
  • レコード番号(type:"RECORD_NUMBER")
  • 作成者(type:"CREATOR")
  • 作成日時(type”:"CREATED_TIME")
  • 更新者(type:"MODIFIER")
  • 更新日時(type:"UPDATED_TIME")
  • ステータス(type:"STATUS")
  • 作業者(type:"STATUS_ASSIGNEE")
  • カテゴリー(type:"CATEGORY")

カテゴリーやプロセス管理を使わない設定でも、内部的にカテゴリー、作業者フィールドは追加されています。
取得した json をビルトインフィールドとビルトインでないフィールドに分けます。

builtin_fields.json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{
  "app": 162,
  "properties": {
    "更新日時": {
      "code": "更新日時",
      "label": "更新日時",
      "noLabel": false,
      "type": "UPDATED_TIME"
    },
    "作成者": {
      "code": "作成者",
      "label": "作成者",
      "noLabel": false,
      "type": "CREATOR"
    },
    "ステータス": {
      "code": "ステータス",
      "enabled": false,
      "label": "ステータス",
      "type": "STATUS"
    },
    "更新者": {
      "code": "更新者",
      "label": "更新者",
      "noLabel": false,
      "type": "MODIFIER"
    },
    "カテゴリー": {
      "code": "カテゴリー",
      "enabled": false,
      "label": "カテゴリー",
      "type": "CATEGORY"
    },
    "作業者": {
      "code": "作業者",
      "enabled": false,
      "label": "作業者",
      "type": "STATUS_ASSIGNEE"
    },
    "レコード番号": {
      "code": "レコード番号",
      "label": "レコード番号",
      "noLabel": false,
      "type": "RECORD_NUMBER"
    },
    "作成日時": {
      "code": "作成日時",
      "label": "作成日時",
      "noLabel": false,
      "type": "CREATED_TIME"
    }
  }
}
fields.json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
{
  "app": 162,
  "properties": {
    "日付": {
      "code": "日付",
      "defaultNowValue": true,
      "defaultValue": "",
      "label": "日付",
      "noLabel": false,
      "required": false,
      "type": "DATE",
      "unique": false
    },
    "文字列__複数行_": {
      "code": "文字列__複数行_",
      "defaultValue": "",
      "label": "業務内容",
      "noLabel": false,
      "required": false,
      "type": "MULTI_LINE_TEXT"
    },
    "文字列__複数行__0": {
      "code": "文字列__複数行__0",
      "defaultValue": "",
      "label": "所感、学び",
      "noLabel": false,
      "required": false,
      "type": "MULTI_LINE_TEXT"
    },
    "ドロップダウン": {
      "code": "ドロップダウン",
      "defaultValue": "",
      "label": "部署",
      "noLabel": false,
      "options": {
        "サポート": {
          "index": "3",
          "label": "サポート"
        },
        "営業": {
          "index": "0",
          "label": "営業"
        },
        "開発": {
          "index": "4",
          "label": "開発"
        },
        "総務": {
          "index": "2",
          "label": "総務"
        },
        "マーケティング": {
          "index": "1",
          "label": "マーケティング"
        }
      },
      "required": false,
      "type": "DROP_DOWN"
    },
    "添付ファイル": {
      "code": "添付ファイル",
      "label": "添付ファイル",
      "noLabel": false,
      "required": false,
      "thumbnailSize": "150",
      "type": "FILE"
    },
    "ラジオボタン": {
      "align": "HORIZONTAL",
      "code": "ラジオボタン",
      "defaultValue": "達成",
      "label": "目標達成度",
      "noLabel": false,
      "options": {
        "達成": {
          "index": "0",
          "label": "達成"
        },
        "未達": {
          "index": "1",
          "label": "未達"
        }
      },
      "required": true,
      "type": "RADIO_BUTTON"
    }
  }
}

フォーム情報を適用する

フォームにフィールドを追加するため、それぞれ次の API を実行します。

  • ビルトインでないフィールド: フィールドを追加する API

    1
    2
    3
    
    curl -X POST -H "$AUTH" -H "$JSON" -d '@fields.json' ${HOST}/v1/preview/app/form/fields.json
    
    {"revision":"4"}
  • ビルドインフィールド: フィールドの設定を変更する API

    1
    2
    3
    
    curl -X PUT -H "$AUTH" -H "$JSON" -d '@builtin_fields.json' ${HOST}/v1/preview/app/form/fields.json
    
    {"revision":"5"}

なお、フィールドを追加した後は、ビルトインでないフィールドも フィールドの設定を変更する API で変更します。

レイアウト情報のコピー

レイアウト情報を取得する

元のアプリから一レイアウト情報を取得します。

1
curl -X GET -H "$AUTH" -H "$JSON" -d '{app:160}'  ${HOST}/v1/preview/app/form/layout.json > layout.json

レイアウト情報を編集して適用する

app を追加し revision を削除して、PUT メソッドで送信します。

1
2
3
curl -X PUT -H "$AUTH" -H "$JSON" -d '@layout.json' ${HOST}/v1/preview/app/form/layout.json

{"revision":"6"}

一覧のコピー

一覧情報を取得する

元のアプリから一覧情報を取得します。

1
curl -X GET -H "$AUTH" -H "$JSON" -d '{app:160}'  ${HOST}/v1/preview/app/views.json > views.json

一覧情報を編集する

取得した views.json の中身は次のとおりです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
  "revision": "7",
  "views": {
    "自分の日報(カレンダー形式)": {
      "date": "日付",
      "filterCond": "作成者 in (LOGINUSER())",
      "id": "9265",
      "index": "1",
      "name": "自分の日報(カレンダー形式)",
      "sort": "作成日時 desc",
      "title": "文字列__複数行_",
      "type": "CALENDAR"
    },
    "日報一覧": {
      "fields": ["日付", "作成者", "文字列__複数行_", "文字列__複数行__0"],
      "filterCond": "",
      "id": "2128",
      "index": "0",
      "name": "日報一覧",
      "sort": "作成日時 desc",
      "type": "LIST"
    }
  }
}

これまでと同様に app を追加して revision を削除します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
  "app":162,
  "views": {
    "自分の日報(カレンダー形式)": {
      "date": "日付",
      "filterCond": "作成者 in (LOGINUSER())",
      "id": "9265",
      "index": "1",
      "name": "自分の日報(カレンダー形式)",
      "sort": "作成日時 desc",
      "title": "文字列__複数行_",
      "type": "CALENDAR"
    },
    "日報一覧": {
      "fields": ["日付", "作成者", "文字列__複数行_", "文字列__複数行__0"],
      "filterCond": "",
      "id": "2128",
      "index": "0",
      "name": "日報一覧",
      "sort": "作成日時 desc",
      "type": "LIST"
    }
  }
}

一覧情報を適用する

PUT メソッドで送信します。

1
2
3
curl -X PUT -H "$AUTH" -H "$JSON" -d '@views.json' ${HOST}/v1/preview/app/views.json

{"revision":"7","views":{"自分の日報(カレンダー形式)":{"id":"5259991"},"日報一覧":{"id":"5259990"}}}
caution
警告

一覧をコピーする際に注意する点としては、以下があります。

  • プロセス管理を使う場合、builtinType=ASSIGNEE の一覧が生成されますが、この一覧は移行できないため、json から除外する必要があります。
  • コピー元で作業者をフィルター条件とした一覧が存在する場合、コピー先でプロセス管理が有効でないとこの一覧を移行できません。

アプリを運用環境に適用する

最後に、deploy.json を使ってアプリを運用環境に適用します。

1
curl -X POST -H "$AUTH" -H "$JSON" -d '{apps:[{app:162}]}' ${HOST}/v1/preview/app/deploy.json

GET deploy.jsonstatus:SUCCESS が帰ってきたらデプロイ成功です。

1
2
3
curl -X GET -H "$AUTH" -H "$JSON" -d '{apps:[162]}' ${HOST}/v1/preview/app/deploy.json

{"apps":[{"app":"162","status":"SUCCESS"}]}

おわりに

いかがだったでしょうか。
デプロイ API を使えば、カスタマイズした JS や CSS の設定や、アクセス権の移行もできます。
今回は同一の kintone へコピーしましたが、別の環境の kintone へもアプリを移行できます。
これまで、手動で行っていたアプリの設定は API を使って行えることになって、運用の手間が省けられるかと思います。