RPAエンジニアの雑記

RPA(Blue Prism)について色々記載してます。

(BluePrism)ポケモン図鑑を作る ~Part15 GASからPOST編~

はーい、おむおむですー。

※2020/07/29追記
newgraduate19-rpa.hatenablog.com

出勤とテレワークが混ざってくると
生活リズムや心身のバランスが崩れてくるので
要注意ですね。抑うつ感ヤバい。

さて、前回はAWSでBlue Prismの実行環境を作りました。
newgraduate19-rpa.hatenablog.com

完全に記事の順番を間違えた感がありますが、
武士に二言はないということで
気を落とさずに続けたいと思います。

今回は、前々回の続きで、GASを完成させたいと思います。

前々回は、Event APIを利用して
GASにPOSTされてきたJSONを調理しました。
newgraduate19-rpa.hatenablog.com

今回は、抽出したメッセージを使用して
APIとして公開されたプロセス
GASから起動するような仕組みを作ります。

SOAP Envelopeの確認

以前の記事で、プロセスの公開方法を説明しました。
newgraduate19-rpa.hatenablog.com

Blue Prismは厳格で伝統があるアプリケーションなので、
今風でチャラチャラしたREST APIなどではなく、
WSDLにキッチリと従うSOAP APIとして公開されます。
f:id:newgraduate19:20200712163347p:plain

というわけで、決められた通りに
SOAP MessageのEnvelopeを作って
RRにPOSTするような仕組みを作っていきたいと思います。

SoapUIで確認すると、
Envelopeの内容は以下の通り。

<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:blueprism:webservice:pokedex">
   <soapenv:Header/>
   <soapenv:Body>
      <urn:Pokedex soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
         <Name xsi:type="xsd:string">ポケモンの名前</Name>
      </urn:Pokedex>
   </soapenv:Body>
</soapenv:Envelope>


また、ヘッダーは以下の通り。

POST http://<ホスト名>:8181/ws/Pokedex HTTP/1.1
Content-Type: text/xml;charset=UTF-8
SOAPAction: ""
Content-Length: 469
Authorization: Basic ユーザ名:パスワード(Base64 Encoded)


この辺を良い感じにGAS内でいじいじしていきます。

POSTする方法

GASでHTTPのPOSTを行う場合、
UrlFetchAppクラスfetchメソッドを使用します。
developers.google.com

デフォルトはGETのようなので、
明示的にPOSTであることを記載する必要があります。

UrlFetchApp.fetch(URL, options)

上の形式で指定するので、
諸々の情報はすべてoptionsの中に入れる必要があります。

これから作っていきましょう。

payloadを作る

まず、POSTのpayload、すなわちbodyにあたる、
SOAP Envelopeを作ろうと思います。

どうやら以前は
GASでSOAP APIをいじるための機能があったようですが、
提供が終了されてしまったようです。
薄情ですね。
gsuite-developers.googleblog.com

かといって、
SOAP APIのエンドポイントからWSDLを取ってきて、
そこからEnvelopeをいちいち作るのは面倒なので、
もうベタ打ちでEnvelope=bodyの内容を作成しようと思います。

Envelopeの作成

まず、SoapUIからEnvelopeの内容をコピペして持ってきます。
そして、入力パラメータのところだけ
Slackのメッセージから抽出した
ポケモンの名前
を渡すよう設定します。

var name = (ポケモンの名前);
var env = '<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:blueprism:webservice:pokedex">' +
  '<soapenv:Header/>' +
  '<soapenv:Body>' +
    '<urn:Pokedex soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">' +
      '<Name xsi:type="xsd:string">' + name + '</Name>' +
    '</urn:Pokedex>' +
  '</soapenv:Body>' +
'</soapenv:Envelope>';


いやぁもうくそダサいですね。

f:id:newgraduate19:20200712180902p:plain

いや、別にいいんですよ?
XML ServiceでEnvelopeを作っても。
developers.google.com

ただ、もう正直見づらいだけで一から作るメリットがなかったので、
こうしたほうがbetterではある、のですが、
なんかこう、うん。。。

optionsを作る

出来ないものはしょうがないです。
気を取り直して、引き続き
ダサい方法でリクエストの種類やヘッダー部分にあたる
optionsを作っていきましょう。

とはいえ、ヘッダーに関しては、SoapUIからコピペ、
というわけにはいきません。
ちょっとだけ工夫します。

まず、概形としてはこんな感じでJSON形式で指定します。
(最小限の内容です)

var options = {
  "method": "post",
  "contentType": "text/xml; charset=UTF-8",
  "headers": {"Authorization": "Basic (ユーザ名):(パスワード)"}
};


HTTPメソッドはPOSTだよ、
POSTのデータはXML形式のテキスト
文字コードUTF-8だよ、
このユーザ名とパスワードでBasic認証するよ、
と指定してあげます。

ただ、いくら検証用環境とはいえ、
Blue Prismのユーザ名とパスワードを
ソース内に平文で打ち込むのは嫌なので、
今回はプロパティストアを使用します。

なお、optionsはあくまでfetchメソッドで使用するパラメータであり、
実際のHTTPリクエストのヘッダーとは厳密には違います。

ので、contentTypecontent-typeなどにしないよう注意が必要です。

プロパティストア

プロパティストアは、GASの機能の一つで、
キーと値のペアを管理できるというものです。
使い方はこちら↓
tonari-it.com
tonari-it.com
今回は、userというキーにユーザ名、
passwordというキーにパスワードを入れます。
f:id:newgraduate19:20200718144346p:plain

この子達を取り出す方法はこんな感じ↓

const prop = PropertiesService.getScriptProperties();
const user = prop.getProperty('user');
const password = prop.getProperty('password');


Base64エンコード

取り出したユーザ名とパスワードは、
Base64でエンコしてあげる必要があります。

こちらはUtilitiesクラス
base64Encodeメソッドで簡単に実装できます。

const prop = PropertiesService.getScriptProperties();
const user = prop.getProperty(user);
const password = prop.getProperty(password);
var bsc = Utilities.base64Encode(user + ':' + password);

これでバッチシです。

完成形

これまでの記事も踏まえ、最終的に
完成したのがこちらになります☆(○分クッキング風)↓

function doPost(e) {
  // Slackの投稿メッセージ取得
  var params = JSON.parse(e.postData.getDataAsString());
  var msg = params.event.text;
  // プロパティストアから取得
  const prop = PropertiesService.getScriptProperties();
  const user = prop.getProperty('user');
  const password = prop.getProperty('password');
  // Base64にエンコード
  var bsc = Utilities.base64Encode(user + ':' + password);
  // 正規表現作成
  const reg = new RegExp('<' + '.*?' + '>', 'g');
  // 正規表現にマッチした箇所と、残った箇所のトリム
  var name = msg.replace(reg, '').trim();
  // SOAP Envelope作成
  var env = '<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:blueprism:webservice:pokedex">' +
              '<soapenv:Header/>' +
              '<soapenv:Body>' +
                '<urn:Pokedex soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">' +
                  '<Name xsi:type="xsd:string">' + name + '</Name>' +
                '</urn:Pokedex>' +
              '</soapenv:Body>' +
            '</soapenv:Envelope>';
  // options作成
  var options = {
    "method": "post",
    "contentType": "text/xml; charset=UTF-8",
    "headers": {"Authorization": 'Basic ' + bsc},
    "payload": env
  };
  // POST
  UrlFetchApp.fetch('http://<EC2のIPアドレス>:8181/ws/Pokedex', options);
}


次回、最終回!

まとめ

・GASはSOAP APIをスコープに入れていない薄情者
・お高めの焼肉を食べたら元気になりました☆

やりたいことはいっぱい、
時間と体力が足りない(´;ω;`)