Service NowのAPIからデータ抽出した話

この記事は[NTTコムウェア Advent Calendar 2021](https://qiita.com/advent-calendar/2021/nttcomware)の14日の記事です。
手探りでService NowのAPI操作時にハマった事とその解決方法について記載します。
同じ事象に遭遇する方の参考になれば幸いです。

# What is "Service Now"?

Service Now とはワークフローのデジタル化を実現するクラウドサービスです。
社内の働き方を統一＆効率化することで無駄を排除し、オペレーションコストを削減が期待できます。
会社で陥りやすい組織ごとにサイロ化された業務最適化・属人化を打破するDXサービスとして注目されています。DX！DX！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2326736/2a313a40-43d8-c4ea-0ef2-445d22ecba9f.png)

https://www.servicenow.com/

Service Now には API があるため既存システムとの連携やスクリプトで操作自動化なんてこともできます。
<del>退職記事でネタにされるぐらい閉鎖的なシステムよりは</del> いろいろと楽しめます。

https://developer.servicenow.com/

## 画面イメージ
ITSM ( IT Service Management / インシデント管理、問題管理、変更管理など ) の例です。

インシデント一覧画面（ Developer instance より）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2326736/0886acf9-c62d-34a0-9981-e920cd6e41f2.png)

インシデントの詳細画面（ Developer instance より）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2326736/a4ba65e5-ecb3-1e0c-692b-226fc0c61afa.png)



## 自分が遭遇した環境制約

自分達で IT ガバナンスをコントロールできる立場ではなかったので以下のような **けしからん** 制約がありました。

 - サイトがヨーロッパにあるため日本からのネットワークレイテンシが高く全てのWEB操作がもっさりする。
 - 最低限のワークフロー操作に制限されており、カラムの追加や編集、ダッシュボード化などができない。
 - 全レコードの一括ダウンロードができない。
    - データ集計する場合は、手作業による心温まる二重管理帳簿をローカルに作成し、毎回地道に集計してパワポに貼り付けという旧態依然の作業が発生。非DX！非DX！:rage:
 - ワークフローのアップデートはメール通知のみ。必要な情報を探す作業（超大変）が必要。
 - Developper 系の権限が無い。Service Now 上でナウいアプリを作るなどして環境改善ができない。

当時のテナントに Ping するとこんな感じ。

```
c:\>tracert XXXXX.service-now.com

Tracing route to XXXXX.service-now.com [148.139.6.XXX]
over a maximum of 30 hops:

  1    <1 ms    <1 ms    <1 ms  buffalo.setup [192.168.11.1]
  2     3 ms     2 ms     3 ms  softbankXXXXXXXXXXXX.bbtec.net [219.188.230.XXX]
  3     3 ms     3 ms     3 ms  softbankXXXXXXXXXXXX.bbtec.net [219.188.225.XXX]
  4     3 ms     3 ms     3 ms  10.9.203.XXX
  5     3 ms    65 ms    68 ms  218.100.7.XXX
  6   429 ms   429 ms   428 ms  xe11-3-0-XXX.AMS.router.colt.net [212.74.68.XXX]
  7   428 ms   428 ms   428 ms  xe11-3-0-XXX.AMS.router.colt.net [212.74.68.XXX]
  8   428 ms   428 ms   428 ms  213.208.224.XXX
  9     *        *        *     Request timed out.
 10     *        *        *     Request timed out.
 11     *        *        *     Request timed out.
 12   428 ms   428 ms   428 ms  vip-148-139-6-XXX.cust.service-now.com [148.139.6.XXX]

Trace complete.
```

 
# やりたいこと

 - 参照系操作はサクサクと動くようにしたい。
 - 自分たちが対応している/したインシデントの統計を知りたい。ダッシュボード化したい。
    - データ連携により自動化。Webを開くだけで今の状態が分かるように。
 - テキストデータに形態素解析などをかけて <del> いろいろ遊びたい</del> 知見を得たい。
    - せっかく顧客の生の情報があるなら活用したい。
 - リアルタイムにステータスの変化、アサイン先の変化等をトレースしたい。
    - その中でも関係するものは個別に対象者へPUSH通知したい。人手で情報を探す手間を省きたい。

かっちょいいダッシュボードの例（ Tableau Public から）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2326736/8dc28ff6-7671-7d95-2b04-ac82845068d1.png)

参考元：

https://public.tableau.com/app/profile/tableau.for.customer.experience.service.and.support/viz/IncidentReportforServiceNow/IncidentReport


# Service Now API を使った解決策

Service Now は REST API が標準で利用できます。

https://docs.servicenow.com/bundle/rome-application-development/page/build/applications/concept/api-rest.html

データを抽出したかったので Table API を利用しました。

https://docs.servicenow.com/bundle/rome-application-development/page/integrate/inbound-rest/concept/c_TableAPI.html


Table API から Service Now の ITSM 全レコードを抽出し、クローン DB を作りました。
取得したデータを元に前章の各種 "やりたいこと" を実装しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2326736/a8960965-12ec-afd0-9a8e-381b4ac44590.png)



全て Golang で作りました。分散処理の実装が簡単だったので。
本構成の肝になるのが Service Now API からデータを引っ張ってくる「データ連携」の部分です（上記図の真中の箱）


## HTTP Requestの部分の流れ
最新の詳しい情報は[リファレンス](https://docs.servicenow.com/bundle/rome-application-development/page/integrate/inbound-rest/concept/c_TableAPI.html)をご覧ください。
全件取得する場合は以下です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2326736/dc7f1eef-8a53-c7cf-25e1-47a56980158c.png)


# Service Now API 操作時にハマったこと、解決方法
一部は管理者が意図した設計/設定によるものかもしれません。


### 多要素認証（ 2FA ）が要求される　
 - ServiceNow の 2FA は[ TOTP 方式](https://ja.wikipedia.org/wiki/Time-based_One-time_Password)です。
    - Service Now アカウントの 2FA 設定時に表示される16桁の文字列と正確な時刻情報があればコード生成できます。
 - MFA ライブラリを入手します。
    - Golang 例：https://github.com/xlzd/gotp
    - Python 例：https://github.com/pyauth/pyotp
    - Node.js 例：https://github.com/speakeasyjs/speakeasy
- Service Now の API 認証時のパスワードフォームに「パスワード＋ 2FA コード6桁」を送信すると認証できます。
    - 例
        - PW：password
        - 2FA生成コード6桁：123456
        - API 認証に送信するパスワード：password123456
    - リファレンス：https://docs.servicenow.com/bundle/rome-platform-administration/page/integrate/authentication/concept/c_MultifactorAuthentication.html

### 1アカウントあたり同時5セッション以上のアクセスすると30分ブロックされる　
 - 並列スレッドの同時実行上限数を定めること。
 - コントロールプレーンを用意すること

###  1回あたりの取得レコードが一定を超えると JSON or XML parse 失敗
 - パケット欠損 or レスポンス遅延するため。
    - Content-Encoding: gzip などもあまり効果なく…。
 - Service Now API へ HTTP Requerst 送信時に Query parameters の　`sysparm_limit=XXXX`　でレコード数の上限を設定する。
    - 自分 or 相手の国のトラフィックが輻輳する時間帯に発生しやすい。
        - 日本だと昼休み時間とか。
    - 環境に起因するものなので日頃からモニタリングし適切な値を設定すること。
 
### 動的な JSON or XML 配列 
 - Golang のように事前に構造体を定義する必要がある場合は要注意。
 - カラムや値が無いデータが返ってくる状況を受け入れること。
 
### どのカラムの値が更新されたかは分からない
 - レコード単位でわかる。カラム単位は分からない。
    - 過去に取得した同一 SysID レコードと比較するとカラム単位の変化がわかる。
 - 更新された順（最新順）にレコードが欲しいときはService Now API へ HTTP Requerst 送信時に Query parameters に `sysparm_query=ORDERBYDESCsys_updated_on` を加える。
    - 画面表示項目`以外`の値が更新されたレコードも含まれる。
        - 目的に合わせてレコードの処理が必要。更新レコード＝即PUSH通知を実装すると通知地獄になる。


# 大事なお約束ごと
 - システムの規制には何か理由があります。理由があるから規制されています。ルールに基づくものだったり、前例踏襲で決めているだけだったり。
 - システム上規制されていない＝操作してもいい。ではありません。
 - 明文化されたガバナンス、ルールは守ることは原則です。
 - 何か試したいときは自分勝手に判断せず、判断できる人に確認しましょう。
 - 遊ぶときは上手に遊びましょう。

:::note info
本稿に記載されている製品名、サービス名は、各団体の商用または登録商標です。
:::