kintoneでJavaScript内定数的な値をアプリに自由に定義したい

本記事は[kintone Qiita Advent Calendar 2019](https://qiita.com/advent-calendar/2019/kintone)の16日目になります。

kintoneでJavaScriptによるカスタマイズをするとき、定数的なものを定義したい場合は、JavaScriptファイルの上部などに定数宣言をまとめてして運用することが多いと思います。
【追記】
あくまでJavaScriptに定義するようなセキュリティ上問題のない値を書くことを前提としています。JavaScriptにも定義できないようなアクセスキーなどの設定はしないようにお願いします。

例えばこんな感じ。

```js
jQuery.noConflict();

//////////////////////////////////////////////
// Arrayge Point
// target apps ID as number
const TARGET_APP_ID = 1234;
// target apps getting val code as String Array
const TARGET_APP_VAL_ARRAY = ["key", "value"];
//////////////////////////////////////////////
```

ただ、JavaScriptの編集を毎回するのって億劫なので、他のサービスによくある「環境変数定義」画面みたいなものからJavaScript内定数値を読み込めるようにしてみました。

# 環境変数アプリの作成
とにもかくにも定義するためのアプリがないことには始まりません。適当にアプリを作成します。どんな項目を設けても良いのですが、今回はKeyとValueを保持するような一般的な環境変数の定義アプリを作成しました。

![スクリーンショット 2019-12-16 22.13.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102769/24fe6c51-26ff-77f7-03ae-b452abb04b2c.png)

Note項目もメモ用につけましたが、今回のアプリでは必須項目を読み込み対象のカラムと自動的に判定するため、非必須項目であればどんな項目があっても無視されます。

![スクリーンショット 2019-12-16 22.17.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102769/06e14e41-903b-d64f-3cc3-3825a88ad8b6.png)


# 環境変数定義を読み込むJavaScript
環境変数定義を読み込むJavaScriptは以下の通りです。どうしても環境変数アプリのIDだけは必要になりますが、逆に言えばそれだけ設定してくれれば勝手に読み込みます。（PC用です）

```javascript
jQuery.noConflict();

const TARGET_APP_ID = 1234;

function get_env_values(event, body) {
    return kintone.api(kintone.api.url('/k/v1/records', true), 'GET', body).then(function(resp) {
        // success
        if (resp.records.length === 0) {
            return;
        }
        var extra_envs = []
        resp.records.forEach(function(val_json){
            extra_envs.push(val_json);
        })
        event.extra_envs = extra_envs;
    }, function(error) {
        console.log(error);
    });
}

function get_field_required_properties(event, body) {
    return kintone.api(kintone.api.url('/k/v1/app/form/fields', true), 'GET', body).then(function(resp) {
        // Only geting required field.
        var comp_array = [];
        
        Object.keys(resp.properties).forEach(function(key) {
            if ('required' in resp.properties[key] && resp.properties[key].required === true) {
                comp_array.push(resp.properties[key]);
            }
        event.required_comp_array = comp_array;
        });
    }, function(error) {
        console.log(error);
    });
}

```

`get_field_required_properties`関数の中で、URLアクセスをして取得した内容は以下のようなJSON形式になっているため、この中身を読んで、properties内の全項目から、required属性がついている部分だけを取り出しています。

```json
{revision: "4", properties: {…}}
  properties:
    Assignee: {type: "STATUS_ASSIGNEE", code: "Assignee", label: "Assignee", enabled: false}
    Categories: {type: "CATEGORY", code: "Categories", label: "Categories", enabled: false}
    Created_by: {type: "CREATOR", code: "Created_by", label: "Created by", noLabel: false}
    Created_datetime: {type: "CREATED_TIME", code: "Created_datetime", label: "Created datetime", noLabel: false}
    Record_number: {type: "RECORD_NUMBER", code: "Record_number", label: "Record number", noLabel: false}
    Status: {type: "STATUS", code: "Status", label: "Status", enabled: false}
    Updated_by: {type: "MODIFIER", code: "Updated_by", label: "Updated by", noLabel: false}
    Updated_datetime: {type: "UPDATED_TIME", code: "Updated_datetime", label: "Updated datetime", noLabel: false}
    key: {type: "SINGLE_LINE_TEXT", code: "key", label: "Key", noLabel: false, required: true, …}
    note: {type: "SINGLE_LINE_TEXT", code: "note", label: "Note", noLabel: false, required: false, …}
    value: {type: "SINGLE_LINE_TEXT", code: "value", label: "Value", noLabel: false, required: true, …}
    __proto__: Object
  revision: "4"

```

実際に必須項目指定をした`key`と`value`のみに`required`フィールドがついているのがわかります。そこでこの2つの値を返します。

また、本処理は同期的に取得した値を扱いたいので、Promiseを返せるように`kintone.api`のコールバック関数は省略しています。

この2つの関数をそれぞれ呼び出せば、設定したアプリの必須項目をJSON配列で取得することができます。


次に、`get_env_values`関数の中では、環境変数アプリの項目のうち、必須項目であるフィールド名を指定し、その値をJSONで取得しています。値を取得する際には、以下のようなbodyを準備する必要があります。

```javascript
    var body = {
        "app": TARGET_APP_ID,
        "query": "",
        "fields": []
    };
```

基本的に全項目を取り出したいので`query`部は空文字、`fields`に文字列配列で取得したい項目のカラムコードが代入されます。（実際には必須項目取得関数`get_field_required_properties`で抽出できたフィールドのうち、requiredフィールドが付与されている項目の一覧になります。


# 適用してみる
実際に適用をしたい場合は以下のような実装が必要になります。今回は一覧ページを開いた際のイベントをフックして環境変数アプリから取得した値をコンソールに吐き出すだけの簡単なアプリです。

```javascript
(function($) {
   "use strict";
   kintone.events.on("app.record.index.show", function(event) {
    var body_required_properties = {
        "app": TARGET_APP_ID
    };
    
    var body_env = {
        "app": TARGET_APP_ID,
        "query": "",
        "fields": []
    };
    
    get_field_required_properties(event, body_required_properties).then(function() {
        event.required_comp_array.forEach(function(obj){
            body_env.fields.push(obj.code);
        })
        
        get_env_values(event, body_env).then(function() {
           console.log(event.extra_envs);
        });
        
    });
    
   });
})(jQuery);
```

`get_field_required_properties`に値を渡す際には、`body.app`のみで事足ります。ただし、get_env_values関数には`query`や`fields`変数が必要になります。そこで今回bodyを2種類作成しています。

これで、無事該当画面の一覧表示を開いたところ、必須項目となっている「key」と「value」をｔ取り出すことができました。

```json
(2) [{…}, {…}]
  0:
    key: {type: "SINGLE_LINE_TEXT", value: "testkey2"}
    value: {type: "SINGLE_LINE_TEXT", value: "testvalue2"}
    __proto__: Object
  1:
    key: {type: "SINGLE_LINE_TEXT", value: "testkey"}
    value: {type: "SINGLE_LINE_TEXT", value: "testvalue"}
    __proto__: Object
  length: 2
```

# まとめ
一般的なサービスでよくある「環境変数定義」をkintoneのアプリを利用して実現することができました。地味な内容ですが、JavaScriptカスタマイズ時に環境変数を定義する度にJavaScriptを編集するのは手間ですし何より事故る可能性があります。これで少しは安全に、効率的にJavaScript開発に携わることができるようになります。