今回は、コマンドラインHTTPクライアントのcurlを使って、既存のアプリを別のkintoneにコピーしてみたいと思います。
デプロイAPIでアプリをコピーするときのハマりどころについて重点的に説明します。
アプリ情報を取得・設定するAPIやデプロイAPIについて、次のAPIドキュメントを参考してください。
  アプリの設定を操作するkintone REST API
コマンドラインでcurlを利用します。
curlはLinuxやMacOS Xで標準搭載されています。
Windowsでもcygwinなどを使って利用できます。
  curl
     
  
  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
 | 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"}}}
 | 
 
 
   
  
    
      警告
    
    一覧をコピーする際に注意する点としては、以下があります。
- プロセス管理を使う場合、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.jsonでstatus: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を使って行えることになって、運用の手間が省けられるかと思います。