kintoneは優れたツールですが完全ではない?なーんてディスられても動じず、欠点を正直に認めるのもkintoneの憎めないところ。思ってやまない著者です。kintoneの欠点のいくつかはすぐに挙げられます。ブラウザーベースで動いているので、ファイルアップロードの作業が面倒、とか。添付ファイルフィールドに画像データを放り込みまくると、一ユーザーあたり5GBの容量の制限が足かせになってくる、とか。そんな限界を解消するためのささやかなクリスマスプレゼントを皆様にお届けしたいと思います。なに、ちょっとした贈り物です。クリスマスツリーによくぶら下がっている箱のような。箱・・・つまりboxです。今回の記事では私の2019年の失敗事例も公開しているので、ひょっとしたら皆さまのご参考になるかもしれません。世の中にオンラインストレージ製品はたくさんありますよね。その中でもboxが存在感を出しているのはご存じでしょうか。kintoneというプラグインとして公開されています。それを使えばブラウザー上でアップロードなどせず、ドラッグ&ドロップでブラウザー上から操作できちゃうのです。Driveをインストールすれば、Windowsのエクスプローラと同じ操作でbox上にファイルをアップロードできてしまう。boxを使うと無尽蔵(契約プランによる)を誇る容量にファイルを置きまくり。それをkintoneの画面上からに自由に呼び出せる。素晴らしい!上に書いたkintoneの弱点を周囲のツールが補ってくれるいい例です。本稿もそうしたkintoneを補ってくれる一つの例としてお役に立てれば幸いです。networkにも出ています。kintoneを導入できるはずです。ところが、この記事で書かれているのは、一つのレコードに一つのフォルダーを対応させるところまで。kintoneでちょっとしたシステムを作ろうとすれば複数アプリにまたがった構成が必要です。それに応じてboxのフォルダー構成も複数の階層にまたがってしまいます。合同会社アクアビットダム設計なる会社Topへ↑たとえば、大阪と東京に支店がある合同会社アクアビットダム設計があったとします。この会社はダムを独自の技術で製造し、お客様にお納めしている設定です。大阪支店[組織]の長井何某[個人]が担当する担当案件[案件]の施行状況[施工]と湛水状況[湛水]を管理すると仮にしましょう(工程はしょりすぎ。ちなみに最後の工程は水を貯める工程です)。他に顧客マスタがあるでしょうがここは割愛。[組織]と[個人]はアプリではなく、kintoneのアカウントを使用する想定です。この場合3アプリですね。ここでご注文からの流れをkintoneで管理したとしましょう。各アプリの連動はkintoneのアクション機能を使ったとします。ダム完成までにはさまざまな状況を報告していかねばなりません。するとダムの進捗に合わせて写真が大量に溜まっていきます。kintoneの添付ファイルフィールドにファイルをアップしていると、すぐに容量が危うくなりかねません。ここでboxの出番です。ここでboxで写真を管理しようとした場合、box内のフォルダー構成はこのようになると思います。kintoneを思い出してみましょう。プラグイン設定画面にルートフォルダーのIDを設定していましたね。アプリ自体にルートフォルダーのみを作る仕様。kintoneだとちょっとキツイかも。合同会社アクアビットダム設計にboxをTopへ↑ということで、本稿では多段階にわたるboxの連動例をお伝えしたいと思います。その時にしでかしてしまった失敗と、そのリカバリ例もお伝えしたいと思います。話を簡単にするため、合同会社アクアビットダム設計としてのルートフォルダーを設定しておきましょう。支店ごとにフォルダーを設定し、支店の配下に担当ごとのフォルダーも生成しておくと話が早いですね。実際のboxのフォルダー構成はこんな感じ。ここでルートフォルダーのフォルダーIDを取得しておきます。boxの画面から取れます。その状態で、案件アプリに新規レコードを登録します。案件アプリの項目には案件の主管支店と、案件の主担当を指定するフィールドも忘れずに。必須項目にするのでしょう。その理由は、レコードが保存成功後、案件フォルダーを作る際にどこのフォルダーの配下に作成するか決めなければならないためです。boxのフォルダー生成APIについてTopへ↑ここでboxの仕様を押さえておきましょうか。サービスの仕様を確認するには、APIから逆引きしたほうが理解しやすい。いわゆる技術者あるあるです。boxのAPIはこちらのサイトをご覧になると良いでしょう。・・APIレファレンスboxのフォルダー作成の項を読むと、親であるフォルダーのIDがパラメーターとして必須のようです。編集時には親フォルダーのIDは必須ではなくなりますが、もしフォルダーの場所を移動する際は親フォルダーのIDは指定しなければなりません。案件フォルダーを作る際は、親となる担当者フォルダーのIDを把握しておかねばなりません。その上の支店フォルダーも。案件レコードの保存のタイミングで行うべきことは、まず、そのレコードの支店フィールドの値に等しい支店フォルダーを検索することです。その際、基準となるのはルートフォルダーです。ルートフォルダーの下にある支店フォルダーを検索し、そのIDを特定します。間髪入れずに支店フォルダーの配下にある担当者フォルダーを検索します。これは同一担当者が複数支店にフォルダーを持っている場合など、運用も考慮していますが、支店フォルダーのIDを内部で保持できるのであれば、いきなり担当者フォルダーから検索してもよいです。重要なのは案件フォルダーを作成するにはその親フォルダーのIDを事前に必ず保持しておくことです。先ほど、kintoneの案件アプリの支店と担当者の両フィールドは必須でなければならないとしたのには、そういう理由があったのです。これら二つのフィールドの値がないと、案件を保存する際に生成されるべき案件フォルダーの保存先が迷子になってしまうので。続いてはいよいよboxの操作を行います。その前に本稿ではboxの権限周りには踏み込まないことを言っておきます。本当はbox内でアプリを作成し、そのアプリ内で設定した権限を認証しなければならないのですが。kintoneに甘えてしまいましょう。kintoneの処理を流用させていただきましょう。処理の大まかな順序としては以下の通りです。のちに述べますが、このコードは動きません。コードは画像として参考程度に載せます。処理の都合上、この中で別のアプリに更新を行い、その結果が成功した場合にboxフォルダー生成処理を呼び出しています。boxフォルダー生成処理では、まずルートフォルダーから支店フォルダーを検索します。続いて支店フォルダーから担当者フォルダーを検索します。ここでboxの検索の仕様が立ちふさがってきます。boxの検索仕様として、対象の種類、生成時刻、オーナーIDや親フォルダーIDなどは指定できるのですが、肝心の文字列を完全一致で検索できないのです。queryというパラメータがあるにもかかわらず、そこに指定した文字列は曖昧検索として処理されてしまうのです。APIレファレンスつまり、親フォルダーに属する検索対象が複数ありうる場合、検索文字列に工夫が必要です。姓名の間にスペースが入る場合など。権兵衛」を設定してもマッチしません。スペースの前後の文字列で検索するのです。この場合、「長井」または「権兵衛」で検索します。すると前者は二件がヒットし、後者は一件がヒットします。その結果を再度ループして回し、一件ごとにname属性の値が検索文字列に一致するかを確認する。そのような面倒な処理がboxの検索には必要です。このコードも実際は使っていませんが、軽く提示します。これで、担当者フォルダーIDまで求められました。続いてはフォルダーの生成に移りましょう!APIレファレンス生成にあたっては、名前と親フォルダーのIDを指定するだけです。これで、案件レコードが保存されたら案件フォルダーを作成するところまでができました。ここで当初想定していたboxの構成を見てみましょう。三フォルダーがあります。このフォルダーの生成にも実は厄介な問題が潜んでいます。案件のレコードが保存されました。案件フォルダーが生成されました。したら、アクション機能によって施工状況アプリにレコードをコピーし、施工状況アプリでもレコードが保存された瞬間、案件レコードの時と同じように施工状況フォルダーを生成すればええんちゃうの?と思ったでしょう。ところが、フォルダーを生成するには親のフォルダーの指定が必須です。親フォルダー、つまり案件フォルダーをフォルダー生成処理の直前で検索してフォルダーIDを取得しなければなりません。ところがこの親フォルダーの検索取得にはひとつハードルが控えています。そのハードルとは、コンテンツが生成されてから検索可能となるまでに時間がかかる、というものです。boxはなんらかのコンテンツが作成されてから、それが検索可能となるまでにbox内部でindexを構築しており、それに時間が掛かるのです。APIレファレンスには以下のように書かれています。案件フォルダーの生成からすぐ、施工状況アプリのレコードを保存した場合、親となるべきフォルダーが検索できないため、親フォルダーの指定ができないのです。boxを多層構造でkintoneと連動させる場合、この仕様上の制限は現状では避けられません。この制限を回避するため、発想を切り替えました。案件フォルダーが生成された後、同時に配下のフォルダーも作ってしまうのです。APIでは、フォルダー生成が成功した時点で返り値として生成されたフォルダーのIDが得られます。このIDを使えば配下のフォルダーも即時に生成できます。このコードも実際は使っていませんが、軽く提示します。この下の処理ではさらに生成した案件フォルダーのURLを取得し、そのURLやフォルダーIDをkintoneの2アプリに更新して設定しています。ここまででkintoneのapp.record.create.submit.successイベントを見てきました。その結果、実装ができそうです。テストでもフォルダーが意図通りに生成されました。開発用のPCでも、お客様のご担当者様のPCでも。やったと思った安心のかげに潜むのが落とし穴。バグが出てしまったのです。テストではうまく動いていたのに、いざ本番になるとうまくいかない。なんということでしょう!実は、その根本的な原因は今もなお究明できていません。事象としてはboxにAPIリクエストを投げた後、何も戻ってこないのにプログラムが終了してしまいます。httpレスポンスすら帰ってこずに。それも終了する場所がまちまちなのが始末が悪い。複雑なPromiseの構造に加え、referredを混在させたことにも問題があったのかもしれません。この不具合がやっかいなのは、boxからのレスポンスを待つ間、app.record.create.submit.successの結果が完了できないことにあります。その間、ブラウザーは固まってしまい、kintoneを利用されている皆様にはただ困惑が。この不具合の原因がブラウザーにあるのか、box側にあるのか、kintone内部にあるのか、それともPCのスペックにあるのか。ネットワーク環境によるものなのか。ブラウザー上でレスポンスを待つ運用はまずい、という悔いだけは骨身に沁みました。私はその原因を追究するよりもお客様の運用を円滑に進めることを優先しました。その決断として、ブラウザーに依存する実装を止めました。AWSへ処理を移管Topへ↑では、どうすればよいか。幸いなことにkintoneにはWebhookという機能が備わっています。URLを設定できます。Gatewayで設定したURLでした。Gatewayについての説明は割愛しますが、kintoneから受け取ったWebhookのリクエストに含まれるJSONを読み取り、それを後続の処理に渡すことができます。Lambdaを選びましたので、同じAWS上で処理が連携できます。LambdaではNode.jsを使い、ほぼkintoneのkintone.app.create.submit.successで実装したのに近いコーディングを行いました。Lambdaから簡単に使用できるので、それを使えば似たような実装ができるのです。boxのアプリは一から作る必要があります。設定もあれこれ行う必要が生じました。最初、こちらのブログの力も借りました。ありがとうございました。Lambda関数のコードを以下に掲示します。このコードは動いているものを基にいろいろといじっているので参考になると思います。typeはコンテンツの対象。content_typesは検索対象とするプロパティ。limitは検索結果として戻す件数。dataは本稿の場合検索対象(支店,担当者,案件No)のうち、担当者の場合["担当名"]で渡ってくる。エスケープした""で囲む。//95行目で生成した配列にPromiseを設定します。//searchFolder関数へはダミーオブジェクト。//218行目で生成した配列にPromiseを設定します。//kintoneの既存案件アプリを更新するレコードを組み立てます。//リクエストのbody部分を組み立てます。});};なんとか実装Topへ↑いずれにせよ、私が2019年に出した唯一の大きなバグがこれでした。結局、バグが出てから実運用にこぎつけるまでにさらに二カ月ほどの時間をいただきました。お客様にも多大なご迷惑をおかけしてしまいました。Lambdaを介してboxへのフォルダー生成と、レコードに対応するboxのURLとフォルダー番号をkintoneの該当レコードに登録することができました。kintoneの内部にも書かれている通りです。実際それを使わせていただいています。ありがとうございます。以下にコードを載せていますが、疲れてきたのでコード内のコメントは割愛します。実案件ではさらに凝った実装(フォルダー数も階層も本稿の例よりさらに多い)が施されています。古くboxが設定されていないレコードには手作業がたまに発生しているものの、実運用に乗っています。この記事ではそれ以上の情報を出すことはお客様の業務に関わるのでここまでにしとうございます。本稿がkintoneを運用している皆様にとって少しの手助けになれば幸せです。kintone上で大量の添付ファイルに困っていらっしゃる方や、社内ファイルサーバーからの移行でお困りの方。他のPaaSからkintoneへ移行する作業があって、添付ファイルの扱いにお困りの方。弊社では本稿のようなboxとkintoneの連動事例を何例も手掛けております。お困りの際はおっしゃってくださいませ。最後に蛇足ですが、boxの案件で例に挙げた三つのダムは、私が実際に訪れてダムカードを入手した場所です。最後になりましたが、このエントリー作成にあたり、以下の2サイトからの情報を参考にさせていただきました。ありがとうございました。2019の5日目の記事です。Calendarの記事ですから。ほら!きとみちゃん楽しいですよね!https://kintone.cybozu.co.jp/jp/kitomi/日々、お仕事に励むきとみちゃん。きとみちゃんとお仕事をする仲間はとっても個性が豊か。あわてんぼうでドジっ子のきとみちゃんがkintoneに救われる姿は微笑ましいです。ちなみに私は巻物で見積書を出してくださる麻呂な方が好きです。この方のお名前はなんでおじゃる?さて、きとみちゃんがお仕事をする上で助けになる手法はkintoneの他にもさまざまなものがあります。その中の一つが冒頭に書いたコーチングなのです。・相手の学習や成長、変化を促し、相手の潜在能力を解放させ、最大限に力を発揮させる。詳しくはWikipediaの「コーチング」をご覧くださいませ。ビジネスにフォーカスを当てたコーチングの歴史はまだまだ浅いです。ここでお伝えしておかなければならないのは、自己啓発セミナーとは違う、ということです。偉そうにウンチクを述べる私ですが、コーチングを受けた経験は人生で1,2回だけ。Calendarで何を語るというのでしょうか。結論を先に書いちゃうと、kintoneでこんなグラフを作ってみましょう!ということなんですね。上に登場したのは四つの傾向を円グラフにしたものです。それぞれの傾向の文字列にマウスを合わせると、事前に登録しておいたキーワードが出てくる。これ、実は以前、お客様に依頼されて作ったkintoneにChart.jsを組み込んだグラフ生成の仕掛けです。私はコーチングには無知です。kintoneに入力画面を作り、その結果を集計することで、設問に応じた四つの傾向が算出できる、ということを知ったときは新鮮でした。相手をほめる場合の、個人に響くキーワードは4つの傾向に分けられるだそうです。それに合わせて、こんな入力画面を作ってみました。仮に20問の設問としています。それぞれの4つの傾向ごとに5問を設問しました。それぞれの問いごとに・よく当てはまる・当てはまる・当てはまらない・まったく当てはまらないの4種類の答えをラジオボタンで設定しています。設問数を増やすことも可能ですし、設問数を自在に増減させたい、というご要望もあるでしょうね。その場合はサブテーブルを使えばよさそうです。この記事ではサブテーブルではなく、20問に固定したバージョンでお届けしてみます!実際の内容Topへ↑はい。アプリの設定画面です。フォームはこんな感じ。左の文字列フィールドは設問の文字列を入力します。フィールドコードは上から順にquestion_1からquestion_20としています。真ん中のドロップダウンフィールドは4つの傾向を選びます。フィールドコードは上から順にtrend_1からtrend_20としています。右のラジオボタンフィールドはそれぞれの答えを入力する欄です。フィールドコードは上から順にanswer_1からanswer_20としています。続いてはグラフを表示するカスタマイズビューを設定してみましょう。続いてはロジックです。実は、このグラフを作るには以下の二つのJavaScriptファイルを設定するだけ。上に設定したのは、Chart.jsです。NetworkからCDNのページに移動してもらえれば。きとみちゃんでもできますよね?htttps://がダブらないようにだけ気を付けて!続いてグラフ表示のロジックですTopへ↑では続いてきとみちゃんとグラフ.jsの内容を。Codeの画面にコメントを入れています。見にくいので、直に貼ったコードも提供します。右にスクロールしてくださいね。//ここは以下の162行目で呼び出されるチャートのプラグインコンフィグで呼び出される部分です。//afterDatasetsDrawとはプラグインコアAPIとして呼び出されるChart.js内部のフックです。//チャートが描画されている対象のDOM要素です。157行目で定義され、159行目でChartオブジェクトに渡されます。//対象チャートをループしています。データは77行目で一種類で指定していますのでループは一回のみです。//チャートのメタデータを取得しています。データやラベルも含まれています。//チャートのhiddenプロパティがTrueの場合そもそもチャートが描画されません。//メタ要素のデータをループします。//円グラフの中の文字の色です。0)は黒を表しています。//36-37行目で文字の場所を設定するためのフォントサイズを16pxで設定しています。表示フォントのサイズとは別に。//ここでは対象となるデータの合計値を返します。4種類のデータの合計です。//それぞれのデータの値を全体の合計で割り、パーセントの文字列を構築します。//それぞれのデータのラベルです。79行目で定義した4つの傾向のラベルですね。//30行目で設定したラベルの値を計算した位置に表示します。//29行目で設定した値のパーセントの文字列を計算した位置に表示します。//さらに四つの傾向に値を秘密ロジックで精緻化しています。この辺も大人の事情が絡んでいます。//161行目でChartオブジェクトに渡されるデータとラベルと背景色のホバー色や枠の組み合わせです。