見出し画像

[ServiceNow]Variableの値をServer側Scriptで取得する

ServiceNowのVariableを取得する方法は大きく2つあるのですが、これらの使い分けがわかったのでシェアします。(Version: Rome)

前提

サンプルデータ

Variableに関するデータ構造を理解するために、下記のサンプルCatalog Itemとデータを用意しました。以降こちらを前提として話を進めます。

サンプルデータ

Variableの値の保存場所

まずは、Variableの値が格納されているテーブルを確認します。通常のVariableと、Mutli-Row Variable Set内のVariableでは異なるテーブルに値が格納されていることに注意が必要です。

通常のVariableの値の格納
通常のVariableの値は、sc_item_optionテーブルに格納されています。このテーブルにはポイントが2か所あります。青線のフィールドを見てみると、どのRequested Itemと紐づくか?という項目がありません。sc_item_optionレコードとRequested Itemとの紐づけは、sc_item_option_mtomという中間テーブルで管理されています。また、赤線の部分を見ると、Multi-Row Variable Set内のVariableの値が格納されていないことがわかります。これはこの後紹介する別のテーブルに保存されています。

サンプルデータに対応するsc_item_optionテーブルのレコード

(なお、Record ProducerにおけるVariableの場合はquestion_answerてテーブルに保存されます)

Mutli-row variable setの中のvariable
Multi-row variable setの中のVariableの値は、sc_multi_row_question_answerテーブルに保存されています。緑線で示したRow Indexフィールドがポイントです。Multi-row Variable Setでは、一つのReuqested Itemに対し、行ごとに同じVariableがそれぞれ異なる値を持ちます。その行を区別するために必要なフィールドがこいつです。

サンプルデータに対応するsc_multi_row_question_answerテーブルのレコード

なお、通常のVariableとMulti-Rowで完全に別テーブルになっているのはテーブル設計としてイケてないように見えますが、これはMulti-RowがMadridあたりからの後発機能であり、後付けで作られたためであるでしょう。これが別記事で書いているMulti-row Variable Setの不便さに繋がっていると思われます。

これらを前提として、Server側のScriptからVariableの値を取得する方法を見ていきます。

Variableのラベルや表示値が欲しい場合はAPIを使う(実装易)

最もシンプルな方法は、Requested ItemのGlideRecordオブジェクトから参照フィールドをdotwalkするように取得できるAPIを利用する方法です。DOCSのをもとに、サンプルデータのRequested Itemレコードに紐づく全てのVariableを出力するサンプルコードを示します。

メニュー > System Definition > Scripts - Background より実行
-----------------------------------------------------------
var now_GR = new GlideRecord('sc_req_item');
now_GR.get('292fe7bf1b300110142aec23604bcbf4');

var vars = now_GR.variables.getElements(true);

for (var i=0; i<vars.length; i++) {
    var now_V = vars[i];
    if (now_V.isMultiRow()) {
        gs.info(now_V.getLabel() + ":");
        var rows = now_V.getRows();
        for (var j=0; j<now_V.getRowCount(); j++) {
            var row = rows[j];
            var cells = row.getCells();
            for (var k=0; k<cells.length; k++) {
                var cell = cells[k];
                gs.info(" " + cell.getLabel() + ":" + cell.getCellDisplayValue())
            }
        }
    } else {
        gs.info(now_V.getLabel() + ":" + now_V.getDisplayValue())
    }
}
-----------------------------------------------------------
結果
*** Script: Single Line Text:test
*** Script: Select Box:choice1_label
*** Script: Reference:Abel Tuter
*** Script: Multi-Row Variable Set:
*** Script:  Multi-Row SIngle Line Text:row1
*** Script:  Multi-Row Select Box:choice1_label
*** Script:  Multi-Row Reference:Abraham Lincoln
*** Script:  Multi-Row SIngle Line Text:row2
*** Script:  Multi-Row Select Box:choice1_label
*** Script:  Multi-Row Reference:Aileen Mottern

*1 MRVSのタイトルの取得方法
MRVSのタイトルは、DOCSの下記の記載の通り、MRVSGlideElementVariableオブジェクトに対して.getLabel()を呼んであげることで取得できます
> now_GR.variables.<var_name>.getLabel(): Get the label of the GlideElementVariable. For a variable, the label of the variable is returned. For multi-row variable set, the title of the variable set is returned.

*2 .getDisplayValue()はDOCSに明確な記載がない
MRVSの.getCellDisplayValue()は上記DOCSに記載のある公式の関数ですが、通常のVariableの表示値を取得するための.getDisplayValue()は上記DOCSには載っていないので、注意です。(.getDisplayValue()はGlideRecordにも実装されている関数であるため、特に問題ないと思われます)

この方法は、実際にユーザに見えているVariableの表示名(Label)と表示値(DisplayValue)の組を取得したいときに役立ちます

この方法では、MRVSの物理名が取得できない

Variableの表示名(Label)と表示値(DisplayValue)の組を取得したい場合は上記の方法で問題ないのですが、Variableの物理名(Name)と内部値(Value)を取得したい場合、このAPIではうまくいきません

GlideRecordなどの関数を参考に、.getLabel()を.getName()に、.getDisplayValue()を.getValue()に書き換えることで、基本的にはうまくいくのですが、MRVSに対する.getName()だけは、undefinedが返ってきてしまい、うまく取得することができませんでした。

メニュー > System Definition > Scripts - Background より実行
-----------------------------------------------------------
var now_GR = new GlideRecord('sc_req_item');
now_GR.get('292fe7bf1b300110142aec23604bcbf4'); // サンプルRequested ItemのSysID

var vars = now_GR.variables.getElements(true);

for (var i=0; i<vars.length; i++) {
    var now_V = vars[i];
    if (now_V.isMultiRow()) {
        gs.info(now_V.getName() + ":");
        var rows = now_V.getRows();
        for (var j=0; j<now_V.getRowCount(); j++) {
            var row = rows[j];
            var cells = row.getCells();
            for (var k=0; k<cells.length; k++) {
                var cell = cells[k];
                gs.info(" " + cell.getName() + ":" + cell.getCellValue())
            }
        }
    } else {
        gs.info(now_V.getName() + ":" + now_V.getValue())
    }
}
-----------------------------------------------------------
結果
*** Script: single_line_text:test
*** Script: select_box:choice1_value
*** Script: reference:62826bf03710200044e0bfc8bcbe5df1
*** Script: undefined:
*** Script:  multi_row_single_line_text:row1
*** Script:  multi_row_select_box:choice1_value
*** Script:  multi_row_reference:a8f98bb0eb32010045e1a5115206fe3a
*** Script:  multi_row_single_line_text:row2
*** Script:  multi_row_select_box:choice1_value
*** Script:  multi_row_reference:71826bf03710200044e0bfc8bcbe5d3b

MRVSも含め物理名、内部値が欲しい場合はデータを格納しているテーブルに直接取りに行く(実装難)

MRVSも含め物理名、内部値が欲しい場合や、DOCSに記載のない関数を使うのを避けたい場合は、データを格納しているテーブルに直接取りに行くしかありません。その場合のサンプルコードを示します。

メニュー > System Definition > Scripts - Background より実行
-----------------------------------------------------------
var rec_item_sys_id = '292fe7bf1b300110142aec23604bcbf4';

// Multi-Row Variable Set
var mrvs_cells = new GlideRecord('sc_multi_row_question_answer');
mrvs_cells.addQuery('parent_id', rec_item_sys_id);
mrvs_cells.orderBy('variable_set'); // MRVSごとに処理するためソートしておく
mrvs_cells.orderBy('row_index'); // 行ごとに処理するためソートしておく
mrvs_cells.query();
var processing_mrvs_name = ""; // 現在処理中のMRVSの名前を保持

while (mrvs_cells.next()) {
    var mrvs_name = mrvs_cells.variable_set.internal_name.getValue();
    if (mrvs_name != processing_mrvs_name) { // new multi-row variable set found
        processing_mrvs_name = mrvs_name; 
        gs.info(processing_mrvs_name)
    }	
    gs.info(" " + mrvs_cells.item_option_new.name.getValue() + ":" + mrvs_cells.value.getValue());
}

// Variable
var variables = new GlideRecord('sc_item_option_mtom');
variables.addQuery('request_item', rec_item_sys_id);
variables.query();
while (variables.next()) {
    // remove variables included in multi-row variable set (but variable included in variables-row variable set are necessary
    if (variables.sc_item_option.item_option_new.variable_set && variables.sc_item_option.item_option_new.variable_set.type.getValue() == 'one_to_many')
        continue;
		
    gs.info(variables.sc_item_option.item_option_new.name.getValue() + ":" + variables.sc_item_option.value.getValue());
}

-----------------------------------------------------------
結果
*** Script: multi_row_variable_set
*** Script:  multi_row_reference:a8f98bb0eb32010045e1a5115206fe3a
*** Script:  multi_row_single_line_text:row1
*** Script:  multi_row_select_box:choice1_value
*** Script:  multi_row_reference:71826bf03710200044e0bfc8bcbe5d3b
*** Script:  multi_row_single_line_text:row2
*** Script:  multi_row_select_box:choice1_value
*** Script: select_box:choice1_value
*** Script: reference:62826bf03710200044e0bfc8bcbe5df1
*** Script: single_line_text:test

長く&読みづらくなりました。さらに、この方法は内部値を読みに行くため、表示値を取得しようとした場合、ReferenceやList collector、Attachment型であれば実際のデータを格納しているテーブルを見に行ったり、Choiceであれば選択肢を格納しているテーブルを見に行かなけれならず、かなり難しくなることに注意です。

以上です。

この記事が気に入ったらサポートをしてみませんか?