Node.jsでSOAP通信する際の細かい設定

# はじめに
仕事で他サービスのAPIを呼び出す際に、SOAP通信をおこなう必要があったのでそのノウハウを記事にしました。

使用したライブラリは [node-soap](https://github.com/vpulim/node-soap) です。
基本的には、README を参考に実装しています。

# Node.js で SOAP 通信をおこなう

### SOAP client の生成
```javascript

import * as soap from 'soap';

/***** 中略 *****/

  const client = await soap.createClientAsync(wsdlPath, options);
```

公式だと、以下の形でクライアントを生成しています。
私が利用したAPIでは、定められたメソッドを順番に呼び出す必要があったので非同期処理で実装をおこなっています。

```javascript
// README
soap.createClient(url, function(err, client) {
  client.MyFunction(args, function(err, result) {
    console.log(result);
  });
});

// README
soap.createClientAsync(url).then((client) => {
  return client.MyFunctionAsync(args);
}).then((result) => {
  console.log(result);
});
```

### API のメソッドを呼び出す
```javascript
const fisrtResult = await client.MyFunctionAsync(firstArgs, firstOptions, firstHeader);
const secondResult = await client.MyFunction2Async(secondArgs, secondOptions, secondHeader);
```

`client.methodAsync` を利用した場合、返却される結果は [result, rawResponse, soapHeader, rawRequest] の形の配列となります。

取得結果を利用する場合は `firstResult[0]`、送信したタグを確認したい場合は `firstResult[3]` を参照します。

### SOAP の際のパラメータについて
`node-soap` では様々なパラメータをセットして通信をおこなうことができます。

`soap.createClientAsync` でセットされる `options` は、WSDL に関する設定をすることができます。
`useEmptyTag` などがその一例となります。

```javascript
const wsdlOptions = {
  useEmptyTag: true,
}
```

複数の namespace を使用する場合にはオプションを以下のようにすることで実現できる、との情報を見つけましたが、筆者の環境では動きませんでした。
namespace の設定は後述します。

```javascript
const wsdlOptions = {
 　overrideRootElement: {
   namespace: 'xmlns:tns',
   xmlnsAttributes: [{
     name: 'xmlns:ns2',
     value: "http://example.com/xxx/xxx",
   }, {
     name: 'xmlns:ns3',
     value: "http://example.com/xxx/xxx",
   }]
 },
}
```

次に、メソッドに渡すパラメータについて確認します。
`client.MyFunctionAsync` には `firstArgs` でパラメータを、 `firstHeader` で Cookie 情報を渡しました。今回、 `firstOptions` は使用しませんでした。
(* Cookie は利用した API の要件です。)

パラメータは、オブジェクト形式で渡します。

```javascript
const Args1 = {
  tag: 'value',
}

const Args2 = {
  tag: {
    attributes: {
      key: 'key',
      value: 'value',
    },
  },
}
```

上記のオブジェクトを引数として渡すと、 `Args1`, `Args2` はそれぞれ、下記の形になります。

```javascript
// Args1
<tag>value</tag>

// Args2
<tag key="key" value="value"></tag>
``` 

タグの値を配列にすることで、親タグ内に複数の同じタグを並行して設定することができます。

```javascript
const Params = [
  {
    key: 'key1',
    value: 'value1',
  },
  {
    key: 'key2',
    value: 'value2',
  },
];
```

```javascript
<parent>
  <children key="key1" value="value1"></children>
  <children key="key2" value="value2"></children>
</parent>
```

### namespace を拡張する

`client` を生成した後に、下記の処理をおこなうことで namespace を拡張できます。

```javascript
client.wsdl.definitions.xmlns.ns2 = 'http://example.com/xxx/xxx';
client.wsdl.definitions.xmlns.ns3 = 'http://example.com/xxx/xxx';
client.wsdl.xmlnsInEnvelope = client.wsdl._xmlnsMap()
```

タグを namespace から引用したい場合、下記の形にすれば利用できました。

```javascript
const Args1 = {
  "ns2:tag": 'value',
}

const Args2 = {
  "ns3:tag": {
    attributes: {
      key: 'key',
      value: 'value',
    },
  },
}
```

# おわりに
SOAP　自体の知識が乏しかったため、実装に難航しました。
また、 `node-soap` 自体は Cookie を利用したセッションの管理には対応していないようなので、その部分は別に実装することになりました。

レスポンスのヘッダーは `client.lastResponseHeaders['set-cookie']` で取得することができます。

不明点、不備などがありましたらコメントいただけますと幸いです。