ServiceNowで色々な情報をGlideRecordなどで取得する

ほぼ雑記です色々とデータを整理して取得する必要があって効率が良い方法を考え中です

Version: London

# sys_domainのカラムを持っているテーブル一覧を作成する
## Excelで試す

[System Definition] - [Dictionary]でテーブルのカラム一覧を表示することが出来ます自分の環境だとカラムの数は98,457の様です

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193272/5fa88fcf-ce31-4a57-05fa-3c03052f832a.png)

Column name is "sys_domain"を指定してsys_domainを持っているテーブルを一覧表示すると1,360個存在することが分かりました

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193272/4e5e83a2-3bf7-4122-7c0f-3b8d45e01c98.png)

リストのヘッダーを右クリックして[Export] - [Excel(.xlsx)]を選択して…

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193272/17c308dd-5c1a-6f9a-b6ac-5d83ec54c0a0.png)

一応Excelだとこの方法で取得出来ますがこの後で複数のテーブルのデータを色々とjoinしたりして整形したいと思っているのでScriptで簡単に出来ないか考えます

## Scriptで試す

こちらの記事でも書いていた[Scripts - BackGround]に移動して

[ServiceNowでJavaのHttpClientを操作する]
https://qiita.com/20_percent_cooler/items/217666f16d3661a90d65

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193272/2a1067d7-725d-e281-7abe-a02edee2cdd8.png)

以下のコードをRun script(JavaScript execute on server)にコピペして

```
var gr = new GlideRecord("sys_dictionary");
gr.addQuery('element',"sys_domain");
gr.query();
var i = 1;
//var table_list = [];
while (gr.next()) {
gs.info("[" + i + "]," + gr.name + "," +  gr.element);
i++;
}
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193272/647a7b14-35b6-c642-2528-c0557e045497.png)

[Run script]を実行します

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193272/e7c12bc5-21b1-df94-8ba8-a990094bb332.png)

成功したようです

![image.png](https://qiita-image-store.s3.amazonaws.com/0/193272/84d94776-99b4-1ea7-5a1b-7655b8e2e389.png)

### 親テーブルがsys_domainを持っているものも考慮する

このままの結果でも誤りでは無いのですがテーブルをextendしたテーブルの親がsys_domadinを持っている場合にも対応出来るScriptを考えます親ドメインが存在する場合には再帰的に同じfunctionを呼んでいます

```
// get table list
var gr = new GlideRecord("sys_db_object");
gr.orderBy('name');
gr.query();
while (gr.next()) {
  gs.log("check sys_domain:" + gr.name);
  check_sys_domain(gr);
}

// check have sys_domain
function check_sys_domain(gr) {
  var grd = new GlideRecord('sys_dictionary');
  grd.addQuery('name', gr.name);
  grd.addQuery('element', 'sys_domain');
  grd.query();
  if (grd.next()) {
    gs.log("have sys_domain:" + gr.name);
  } else {
    gs.log("have no sys_domain:" + gr.name);
    if (gr.super_class) {
      gs.log("-- check parent:" + gr.super_class.name);
      var grs = new GlideRecord("sys_db_object");
      grs.addQuery('name', gr.super_class.name);
      grs.query();
      if (grs.next()) {
        check_sys_domain(grs);
      }
    } else {
      gs.log("OUT");
    }
  }
}
```

まだ作り途中ですがコピペして置きます

調べるついでに他にも色々と情報がScriptで取得出来たのでコピペして置きます

# 全てのtableを親子関係込みでScriptで出力する

ER図はこちらの記事で出力方法を書いていますが

[ServiceNowでテーブルを確認/作成する]
https://qiita.com/20_percent_cooler/items/a421efce9fcbfe6335b1

テキストで出力した結果を編集などしたかったので以下で試してみましたこちらも親がある場合には再帰的にfunctionを呼んでいます

```
gs.log("**** ***** creating all tables tree...");
var gr = new GlideRecord("sys_db_object");
gr.orderBy('name');
gr.query();
var ok_cnt = ng_cnt = 0;
while (gr.next()) {
  var msg = gr.name;
  check_parent(gr);
}
function check_parent(gr) {
  if (gr.super_class) {
    msg = gr.super_class.name + " - "  + msg;
    var grs = new GlideRecord("sys_db_object");
    grs.addQuery('name', gr.super_class.name);
    grs.query();
    if (grs.next())
      check_parent(grs);
  } else {
    gs.log(msg);
  }
}
```

# 全てのtableを親子関係込みでACLとroleもScriptで出力する

テーブルのACLとACLについているroleも出してみました

```
gs.log("**** ***** creating all tables tree and ACL...");
var gr = new GlideRecord("sys_db_object");
gr.orderBy('name');
gr.query();
while (gr.next()) {
    var msg = gr.name;
    check_parent(gr);
    getACL(gr);
}
function check_parent(gr) {
    if (gr.super_class) {
        msg = gr.super_class.name + " - "  + msg;
        var grs = new GlideRecord("sys_db_object");
        grs.addQuery('name', gr.super_class.name);
        grs.query();
    if (grs.next())
        check_parent(grs);
    } else {
        gs.log(msg);
    }
}
function getACL(gr){
    var gsa = new GlideRecord("sys_security_acl");
    var qc = gsa.addQuery('name', gr.name);
    qc.addOrCondition('name','STARTSWITH', gr.name + ".");
    //gsa.addQuery("name", gr.name);
    gsa.query();
    while(gsa.next()){
        var scpt = gsa.script;
        var gsar = new GlideRecord("sys_security_acl_role");
        gsar.addQuery("sys_security_acl", gsa.sys_id);
        gsar.query();
        if (gsar.getRowCount() == 0){
            gs.log(msg + " : " + gsa.name + ":" + gsa.operation); //+ " - " + scpt);
        } else {
            while(gsar.next()){
                gs.log(msg + " : " + gsa.name + ":" + gsa.operation  + " - " + gsar.sys_user_role.getDisplayValue()); //+ " - " + scpt);
            }
        }  
    }
}
```

# 全てのroleをcontain含めてScriptで出力する

roleの一覧も欲しくなったので作ってみましたroleの一階層目のcontainに全てのroleが含まれているという前提があっていれば全てのcontainが出ていると思われます(未確認です)

```
gs.log("**** **** creating all roles tree...");
var gr = new GlideRecord("sys_user_role");
gr.orderBy('name');
//gr.addQuery('role', 'itil');
gr.query();
while (gr.next()) {
  var msg = gr.name.getDisplayValue();
  gs.log(msg);
  get_contain_roles(gr);
}
function get_contain_roles(gr) {
  var grr = new GlideRecord('sys_user_role_contains');
  grr.addQuery('role', gr.sys_id);
  grr.query();
  while(grr.next()){
    gs.log(msg +  " - " + grr.contains.getDisplayValue());
    //get_role(grr);
  }
}
function get_role(grr){
  var grrr = new GlideRecord("sys_user_role");
  grrr.addQuery('sys_id', grr.contains);
  grrr.query();
  if (grrr.next()) {
    get_contain_roles(grrr);
  }
}
```
# Appendix

以降はメモ置き場です

## 受信したメールのfrom_addressとcopiedのメールアドレスでsys_userのemailを検索して見つかった場合一番古い値を設定して見つからない場合は元のメールアドレスを配列に設定

こういう場合で

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193272/79d63fc1-2773-4812-9bce-59984de3b6eb.png)

これで

```
var from = "maguro@example.com";
var cc = ["molamola@example.com", "namerou@example.com", "unknown@example.com"];

var gru = new GlideRecord('sys_user');
var gru_qc = gru.addQuery('email', from);
if (!gs.nil(cc))
  gru_qc.addOrCondition('email', 'IN', cc);
gru.orderBy('sys_created_on');
gru.query();
var obj_gru = new Object();
if (!gru.hasNext()) {
  gs.info("no record");
  //return from + cc;
}
while (gru.next()) {
  obj_gru[gru.email] = gru.name.toString();
}
gs.info( JSON.stringify(obj_gru));
var arr_from_cc = new Array();
arr_from_cc.push(from);
if (!gs.nil(cc)) 
  arr_from_cc = arr_from_cc.concat(cc);
var arr_ret = new Array();
for (var i=0; i<arr_from_cc.length; i++) {
  if (!gs.nil(obj_gru[arr_from_cc[i]])) { 
    arr_ret.push(obj_gru[arr_from_cc[i]]);
    continue;
  } 
  arr_ret.push(arr_from_cc[i]);
}
gs.info(arr_ret);
//return arr_ret;
```
こう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193272/e3d22c74-4439-47a4-a215-bb4496393d35.png)

削れる配列を削って更に短く

```
var from = "maguro@example.com";
var cc = ["molamola@example.com", "namerou@example.com", "unknown@example.com"];

var gru = new GlideRecord('sys_user');
var gru_qc = gru.addQuery('email', from);
if (!gs.nil(cc)) //if Cc exist
  gru_qc.addOrCondition('email', 'IN', cc);
gru.orderBy('sys_created_on');
gru.query();
if (!gru.hasNext()) {
  gs.info("no record"); //**** debug
  //return from + cc;
}
var obj_gru = {}; //new Object() for GlideRecord.
while (gru.next()) {
  obj_gru[gru.email.toString()] = gru.name.toString(); //have no same key (key is “email”, value is “name”).
}
gs.info(JSON.stringify(obj_gru)); //**** debug
var arr_from_cc = [from]; //new Array() for return value.
if (!gs.nil(cc)) //if Cc exist
  arr_from_cc = arr_from_cc.concat(cc);
gs.info(arr_from_cc.toString()); //**** debug
for (var i=0; i<arr_from_cc.length; i++) {
  if (!gs.nil(obj_gru[arr_from_cc[i]])) //if “name” exist
    arr_from_cc[i] = obj_gru[arr_from_cc[i]]; //change
}
gs.info(arr_from_cc); //**** debug
//return arr_from_cc;
```