弊ロジカルスタジオでは、エンジニアの業務ごとにチームに分かれて活動しています。 バックエンドチームでは、活動の一環としてMonthlyを発信しています!
日々の業務でぶち当たったことや疑問に思ったこと、調べたことをベースに、 自己学習やたまたま見かけた面白い情報なども交えながら、情報共有できればと思っています。
今月からこちらのブログにて紹介いたしますので、 ぜひご覧ください!
目次
1.【React】Kintoneの全アプリ情報を取得する方法
kintoneでは複数のアプリを取得することができる「kintone REST API(参照)」が用意されています。
ただし、このAPIで一度に取得できるアプリの最大件数は 100 件までです。
そのため、アプリが100件を超える場合には、全てのアプリを取得するために少し工夫が必要になります。
以下の例では、再帰を利用してすべてのアプリ情報を取得しています。
const fetchAllApp = async (): Promise<App[]> => {
  const result: App[] = [];
  const fetchAppRecursive = async (offset: number) => {
    try {
      const response = await client.app.getApps({ offset });
      result.push(...response.apps);
      if (response.apps.length === 100) {
        await fetchAppRecursive(offset + 100);
      }
    } catch (error) {
      console.error("Error App data from kintone:", error);
      throw error;
    }
  };
  await fetchAppRecursive(0);
  return result;
};
// 全てのアプリ情報が欲しいところで呼び出す
const apps = await fetchAllApp();
ここで重要なのは、getApps メソッドのリクエストパラメーターである offset です。
offset は「先頭から何件スキップして取得するか」を指定するため、最初は0を指定して0〜99件目のアプリを取得します。
もし取得件数がちょうど100件だった場合、まだ続きがある可能性が高いため、offset に100を加えて再度リクエストを行う、という仕組みです。
なお、この「一度に処理できるのは最大100件まで」という制限は、アプリ情報取得APIに限らず、kintoneの「取得・更新」系APIに共通しています。
同じ考え方を使えば、他のAPIに対しても大量のデータを扱うときに応用できますので、ぜひ試してみてください!
2.Dockerコンテナを監視して、ログをSlackへ通知しよう
Dockerを使っているときにログを監視したいというケースは少なくないと思います。
例えば某案件のケースでは、AWSのSESを使用してメールを送信していた本番環境の代わりに、
LocalStackというものを使用し、SESをエミュレートし開発を行っていました。
LocalStackではdocker logsにメールの送信内容が吐き出される(実際にメールは送信されない)ため、stg環境でメールの検証を行うのが困難でした。
そこで、「LocalStackのコンテナを監視し、特定の条件にヒットするログをSlackに飛ばす機構を作成しよう」となり、実際作成したので公開します。
ここで重要視されたのは下記です。
- Dockerで動作すること
 - 独立したDocker定義にできること
 
まず1つ目のDockerで動作することについてですが
stg環境がDockerにて動作しているため、追加でソフトウェアの導入が不要であり、なおかつ現状の環境を汚さないという点で挙げました。
2つ目の独立したDocker定義にできることですが、
ここで定義するコンテナは開発環境では不要です。
したがって「環境によって必要なケースが有り、その場合のみ使用したい」というものでした。
よって開発用のdocker-composeには含めたくなく、このためにdocker-composeをstg用に作成するのも手間だったので、socketを共有する方法にて実装しました。
FROM ubuntu:latest
RUN apt-get update && \\
    apt-get install -y docker.io curl && \\
    mkdir -p /app
COPY monitor-stdout.sh /app/monitor-stdout.sh
RUN chmod +x /app/monitor-stdout.sh
CMD ["bash", "-c", "docker logs -f localstack | /app/monitor-stdout.sh"]
monitor-stdout.shはこちら
#!/bin/bash
# スクリプト起動時刻を記録(ISO 8601形式)
START_TIME=$(date -u +%Y-%m-%dT%H:%M:%S)
echo "START MONITOR"
echo "Start time: $START_TIME"
# 接続通知を送信
curl -X POST -H 'Content-type: application/json' --data "{\\"text\\":\\"🔌 メール監視を開始したよ!\\"}" SlackのWebhook
# 標準出力を監視
while read -r line; do
    # タイムスタンプを抽出して比較
    if [[ $line =~ ([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}) ]]; then
        log_time="${BASH_REMATCH[1]}"
        if [[ "$log_time" > "$START_TIME" ]]; then
            if echo "$line" | grep -q "AWS ses.SendEmail"; then
                echo "$line"
                # Slackに通知を送信(シンプルな形式)
                curl -X POST -H 'Content-type: application/json' --data "{\\"text\\":\\"📧 メール送信を検出したよ!\\n\\`\\`\\`$line\\`\\`\\`\\"}" SlackのWebhook
            fi
        fi
    fi
done
echo "END MONITOR"
起動
docker run \\
  -v /var/run/docker.sock:/var/run/docker.sock \\
  kindai-log-monitor
ポイントはdocker runのときにdocker.sockをvolumeとして指定することくらいでしょうか。
monitor-stdout.sh については使用用途によって書き換えていただければと。
スクリプト起動時刻を保持しておいて、それ以降のものだけ出力するようにしないと、過去のログまで出力されるので要注意です!
今回はメール送信テストのために作成しましたが、本番環境のログを監視しておけばエラーにも対応可能でしょうし、docker logsではなくexecでログファイルを取得する手法もありだと思います!
アイディア次第で使用用途は広がりますし、何より現状のDocker定義に手を加えなくてよいのがGoodですね!
3.【PowerCMSⅩ】PowerCMSⅩのバイナリ命名仕様に翻弄された話
PowerCMSⅩには「モデル」という概念があります。DBのテーブルのようなものです。
モデルにはカラムを自由に定義できます。
文字列・数値・日付などいろいろありますが、その中には「バイナリ型」というのもあります。
「バイナリ型」には画像やPDFなどのファイルを保存できます。
モデル「test」のカラム設定

モデルのデータを作成し、バイナリ型のカラムにファイルをアップロードして保存すると
ファイルの公開URLが自動で生成されます。

ここのURLの末尾のファイル名なのですが、自分で自由にファイル名を決めることはできません。
PowerCMSⅩの仕様で、必ず以下のように決まります。
- モデルに「Basename」というカラムが存在する場合
- 最初にファイルが存在するバイナリカラムのURLには『
ベースネーム.xxx』が適用される 
 - 最初にファイルが存在するバイナリカラムのURLには『
 - それ以外
- 『
モデル名-カラム名-オブジェクトID.xxx』が適用される 
 - 『
 
なので、Basenameとバイナリ1~バイナリ3がある場合
バイナリ3にだけファイルをアップロードすると、ベースネーム.xxx でURLが生成されます。
その後、バイナリ2にファイルを追加すると、そちらが ベースネーム.xxx を引き継ぎ、
バイナリ3は モデル名-カラム名-オブジェクトID.xxx に変わる、という挙動になります。

この仕様を知らなかったので、かなり翻弄されました。
私の場合は、バイナリ3のファイル名が必ず モデル名-カラム名-ID.xxx の形式になるようにしたかったのですが、
いざプラグインの実行をしてデータを連携すると basename.xxx になってしまい
調べても情報が出てこず、かなりの時間を費やしました。
最終的にはバイナリ3以前のバイナリカラムには必ず手動でファイルが設定される運用で、バイナリ3のファイル名は必ずモデル名-カラム名-オブジェクトID.xxx の形式になるということがわかり、解決しました。
バイナリファイルのURLの命名はカラムの順序が影響するため、モデル定義の設計時に注意が必要です。
また、PowerCMSⅩのプラグイン開発などで、URLのパターンに依存する処理を書く場合はこの仕様を考慮する必要があります。
PowerCMSⅩのバイナリカラム関連で悩んだときに、この記事を思い出してもらえると嬉しいです。
4.【kintone】『ルックアップの参照先から値をコピーできません。「コピー元のフィールド」に指定したフィールドの設定で「値の重複を禁止する」を選択しておく必要があります。』とエラーが出るときの解消法
どんな現象?
連携系のプラグインで、別アプリのルックアップフィールドに値をコピーしようとした際に頻出するエラーです。
例として、売上管理アプリから商品マスタを商品名のルックアップフィールドで取得し、 同じく商品名のルックアップフィールドを持つ在庫管理アプリにプラグイン等でレコードを作成するケースを考えてみます。

このとき、②でタイトルのエラーが発生します。
在庫管理アプリにレコードを発行する際、 商品名のルックアップフィールドに入力された情報で商品マスタから取得しようとします。
しかし、この例では商品マスタの商品名は重複可のため、 商品名だけではどのレコードを取得すればいいかが判別できません。
そのため在庫管理にレコードを作成できずエラーが発生します。
解決法①
まず、シンプルな解決法は以下の2つです。
- ルックアップ元(今回の場合、商品名)を重複不可に変更する
 - ルックアップ元を重複不可フィールド(商品コード)に変更する
 
しかしどちらも要件次第で、 前者は同一商品名の複数商品を管理(別途カテゴリなどで差別化)したい場合には使えませんし、 後者は商品名でルックアップを取得したい場合には使えません。
解決法②
ルックアップ元は重複可にしておく必要がある状態でこのような操作を行いたい場合、 TIS製の以下プラグインが便利です。
このプラグインでは、ルックアップ元のフィールドとルックアップで検索を行うフィールドを別々に設定することができます。
解決法①の折衷案のようなイメージで、ルックアップ元は商品コードにしつつも、検索は商品名で行えるようになります。
一応、デメリットとしては以下が挙がりますが、いずれも影響は少ないでしょう。
- ルックアップ選択時のデザインが若干変わる
- kintone標準のものではなく、プラグインの機能による選択になるため
 
 - 商品名だけでなく、商品コードもフィールドを用意して取得する必要がある
 
5.【VSCode&Markdown】アンダースコアがエスケープされるのが気になったので無効にした。
Markdownのファイルを編集する機会が多くなってきました。
編集には主にVSCodeを使用しています。
便利な拡張機能も多いため、主に下記を使用しています。
- Markdown All in One
 - markdownlint
 - Prettier – Code formatter
 - Markdown Preview Github Styling
 
上記の環境で基本的に困ることは無いのですが、少し気になる挙動がありました。
下記の見出しが有った時、ファイル保存時の自動整形(Prettier)を有効化していると、
## 設計_テーブル設計
下記に整形されて保存されます。
## 設計\\_テーブル設計
<!-- ↑アンダースコアがエスケープされている。-->
アンダースコアをイタリック記号と認識しているため発生している可能性が高そうでした。
自身の環境ではこのような見出し中のエスケープは不要なため、無効にできないか調査したところ、PrettierはCommonMark仕様に従ってアンダースコアをエスケープするような情報が出てきました。
The implementation is highly compliant with the CommonMark spec, and backed by the excellent remark-parse package.
*.md のファイルに対して自動保存や全整形ルールを無効にする方法はありましたが、アンダースコアのエスケープのみをピンポイントで無効にする方法が見つからず、Prettier 用のタグを入れることで無効にしました。
<!-- prettier-ignore -->
## 設計_テーブル設計
prettier-ignoreタグを書いた直後の行だけ自動整形の対象外になります。
もしMarkdown以外でも、特定の自動整形を無効にしたいことがあったら、上記方法も試してみてください。
(見出しが出てくる度にタグを書くのが面倒なのが欠点)
最後までお読みいただきありがとうございました。
Backend Team Monthly は今後も更新予定ですので、お楽しみに!
また、ロジカルスタジオでは、エンジニアを募集中です。
ぜひ下のバナーから確認してみて下さい!


