もふもふ技術部

IT技術系mofmofメディア

RailsアプリケーションとKintoneを連携する

CMS(顧客管理システム)で一番有名なのはセールスフォースですが、国内製品だとサイボウズが提供する Kintone が有名です。
操作性もよく、個人的にはセールスフォースよりずっと使いやすいんじゃないかと思ってます。
今回は、業務で Rails アプリケーションから Kintone へ連携する機能の実装を行なったので、その方法について解説してみたいと思います。

ドキュメント

Kintone には開発者ネットワークというものが存在していて、これに登録すれば開発用 Kintone を無料で使うことが出来ます。 ライセンス期間は 1 年で、毎年更新すれば今のところはずっと無料で使えます。
また、ドキュメントも充実しているので、基本的にこれを見れば大体のことは解決出来ると思います。

Kintone では、登録すると独自のサブドメインが発行され、その中で色々なアプリを作ることが出来ます。
Kintone のアプリとは、要するにデータベースの 1 テーブルだと思って頂いて構いません。

SDK

Kintone には色々な言語の SDK が用意されていますが、Ruby ではサイボウズ公式 SDK は存在せず、有志で開発された SDK が存在します。

kintone REST API client for Ruby

個人で開発されたものですが、動作には全く問題が無いのでこちらを使って実装を進めていきます。 最近の PR もあるので、メンテされているものと信じましょう。 使い方は Github の READ.ME を読めばわかるので、基本的な CRUD 機能の使い方と注意点だけ解説することにします。

使い方

まず、gem をインストールします。

# Gemfile

gem 'kintone'
$ bundle install

認証

Kintone API の認証方法は 2 通りのやり方があります。

  1. ユーザー ID とパスワードで認証する
  2. アプリごとに発行するトークンで認証する

API トークンの発行方法については下記の記事を参考にしてください。
API トークンを使ってみよう

それぞれメリットデメリットがあります。

  1. ユーザー ID とパスワードで認証する
    メリット:複数アプリの連携を行う際も使い回し出来る
    デメリット:セキュリティ的に心配、実行する API の権限を設定出来ない

  2. アプリごとに発行するトークンで認証する
    メリット:セキュリティ的に安心、API の権限を設定出来る
    デメリット:複数アプリとの通信を行う場合は各アプリのトークンを管理しないといけない

それぞれ下記のようにクライアントを作成します。

# ユーザーIDとパスワードで認証
cybozu_domain = 'example.cybozu.com'
user_id = 'Administrator'
password = 'password'
client = Kintone::Api.new(cybozu_domain, user_id, password)

# APIトークンで認証
token = 'authtoken' # ランダムな文字列
client = Kintone::Api.new(cybozu_domain, token)

レコード取得

1 レコードの GET についてです。まず、対象となるアプリ ID とレコード ID 対象にを取得します。
アプリ ID は下記の写真のように、https://xxxxxxxxxxxx.cybozu.com/k/の後についている数字です。

kintone url

app = 9; id = 100
api.record.get(app, id)

# 返却値
# {
#   "record" => {
#     "record_id" => {
#       "type" => "RECORD_NUMBER", ← フィールドタイプ
#       "value" => "1"  ← フィールド入力値
#     }
#   }
# }

複数レコードを取得することも出来ます。 fileds を配列で指定することで、特定のカラムの入力値だけ取得することも出来ます。
query を取得することで、検索条件を指定出来ます。
指定できる演算子はフィールドタイプによって異なるので、こちら「query」パラメータで利用可能な演算子と関数を参照してください。

app = 8; fields = ["record_id", "created_time", "check_box"]
query = "updated_time > \"2012-02-03T09:00:00+0900\" and updated_time < \"2012-02-03T10:00:00+0900\" order by record_id asc limit 10 offset 20"

# 複数レコードを操作する場合はrecordではなくrecordsを使う
api.records.get(app, query, fields)

# 返却値
# {"records" => [
#     {
#       "record_id" => {
#         "type" => "RECORD_NUMBER",
#         "value" => "20"
#       },
#       "created_time" => {
#         "type" => "DATETIME",
#         "value" => "2012-02-03T09:10:00+0900"
#       },
#       "check_box" => {
#         "type": "CHECK_BOX",
#         "value": ["A", "B", "C"],
#       }
#     },
#     ...
#   ]
# }

レコード登録

1 レコードの POST についてです。
レコード登録時は下記のように{"フィールド名" => { "value" => "値" }}という形でリクエストを送る必要があります。
しかし、Kintone::Type::Recordにフィールド名とフィールド値の hash を与えるだけでよくなります。

app = 9
record = {"number" => {"value" => "123456"}}
api.record.register(app, record)

# Kintone::Type::Recordを使うと
record = Kintone::Type::Record.new(number: "123456")
api.record.register(app, record)

# 返却値
# {"id" => "100", "revision" => "1"}

Bulk insert の場合は配列を渡してあげれば OK。

app = 9
records = [
  Kintone::Type::Record.new(number: "123456"),
  Kintone::Type::Record.new(number: "789012"),
  Kintone::Type::Record.new(number: "345678")
]
api.records.register(app, records)

# 返却値
# {"ids" => ["100", "101"], "revisions" => ["1", "1"]}

レコード編集

レコード編集時は、編集するレコードのidか、もしくは重複禁止フィールドコードと値によるupdateKeyを指定する必要があります。詳しくはこちらリクエストパラメータの項目を参照してください。
しかし、kintone REST API client for Rubyidを指定した編集しか想定しておらず、updateKeyを指定することが出来ません。
ただし、複数レコード編集の場合はupdateKeyでも問題なく動作するので、updateKeyを使いたい場合は複数レコード編集のリクエストを投げるようにしましょう。

単一レコード編集の場合

app = 9
id = 1

record = Kintone::Type::Record.new(string_multi: "changed!")
api.record.update(app, id, record)

# 返却値
# {"revision" => "2"}

複数レコードの場合

app = 9

# id指定の場合
records = [
  {
    id: 1,
    record: Kintone::Type::Record.new(string_multi: "abcdef")
  },
  ...
]

# updateKey指定の場合
records = [
  {
    updateKey: { field: 'type', value: 'will_update' },
    record: Kintone::Type::Record.new(string_multi: "abcdef")
  },
  ...
]
api.records.update(app, records)

# 返却値
# {"records" =>
#   [
#     {"id" => "1", "revision" => "2"},
#     ...
#   ]
# }

レコード削除

削除の場合は一括削除のリクエストを投げれば OK

app = 8; ids = [1,2,3,4,5]
api.records.delete(app, ids)

# 返却値
# {}

まとめ

この gem を使えば一通りやりたいことが出来るかなという印象でした。
レコード個別編集時にupdateKeyが使えないのが個人的にハマりポイントで、そこは gem 側でちゃんと対応してほしいなと思っています。 この記事では解説しませんでしたが、エラー発生時のメッセージも結構適当なので、もしかすると自分でクライアント作った方がいいかもしれませんね。