社内ネットワークからIBM Watsonにつながらない

#はじめに
社内ProxyのおかげでIBM WatsonのAPIが通らかったので対処法を記載します。
(今回はWatson Discoveryです)

#結論
node-sdkのところに書いてありました。:open_hands:
[Use behind a corporate proxy](https://github.com/watson-developer-cloud/node-sdk#use-behind-a-corporate-proxy)

```
npm install tunnel
```

#前提条件
+ Node v10.16.3
+ npm v6.9.0
+ IBM CloudのWatsonDiscoveryのサービスが既にあり、collectionが作成済みであること 

Discoveryについてはこちらの記事が参考になると思います。
Discoveryってなんぞ？という方はまずこちらの記事を見ることをおすすめします。
[【2019/2月 全面更新!】Watson Discovery Serviceが日本語対応したので、触ってみた【SDUやってみた】編](https://qiita.com/ishida330/items/bb1bd4cf61da9b9c6da2)

社内ProxyのせいでAPIが通らないのなら、Nodeやnpmもインストールできないのでは・・・？
というご意見はごもっともですが、Node,npmはすでに実行できるという前提でお願いします。今回はあくまでIBM Watsonに焦点を当てます。

#まずはWatsonのAPIを実行してみる
好きなディレクトリにて

```
npm init
```

何か聞かれますがすべてEnterでOK

##Watson APIs Node.js SDK　のインストール
Discoveryのインスタンスをクリックすると次のような画面が出ると思います。
その画面のAPIリファレンス→Nodeタブへ移ると記載があります
![top.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649992/f0ffda5e-c25d-2b73-5043-315bc1927cca.png)
![apire.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649992/1b5d8d95-9660-685b-6a80-be40869d2b92.png)

```
npm install ibm-watson@^5.7.0
```
※実際のバージョンと違う場合があるため、確認することをお勧めします。

##APIの実行
まずは簡単に今あるCollectionのリストを表示してみましょう。

APIリファレンスでは左側のMethods>Collection>List collectionのところに記載があります。
丁寧に右側にExample requestがあり、クリックでコピーができるようになっているのでコピーしましょう。
実行するファイルは適当に```collection.js```にします。

```javascript:collection.js
const DiscoveryV1 = require('ibm-watson/discovery/v1');
const { IamAuthenticator } = require('ibm-watson/auth');

const discovery = new DiscoveryV1({
  version: '2019-04-30',
  authenticator: new IamAuthenticator({
    apikey: '{apikey}',//自身の情報に書き換え
  }),
  serviceUrl: '{url}',//自身の情報に書き換え
});

const listCollectionsParams = {
  environmentId: '{environment_id}',//自身の情報に書き換え
};

discovery.listCollections(listCollectionsParams)
  .then(listCollectionsResponse => {
    console.log(JSON.stringify(listCollectionsResponse, null, 2));
  })
  .catch(err => {
    console.log('error:', err);
  });
```

しかし、このままでは実行してもエラーが出てしまいます。
**どこ**のDiscoveryの**どの**インスタンスに対してAPIを呼ぶかが記載されていないからです。
上記ソースコードの「//自身の情報に書き換え」をそれぞれ書き換えます(中括弧もいらない)

apikey,url
![api,url.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649992/5248e88b-4a28-ba14-0d46-175488772cb6.png)

environment_id
![environ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649992/93455362-1790-1abe-1fd7-4d0bc340020b.png)

書き換えた後に実行

```
node collection.js
```
そうすると無事エラーが返ってきます。:v:

```
error: { Error: connect ETIMEDOUT 23.41.93.168:443
    at RequestWrapper.formatError (xxxxxxxxxx\node_modules\ibm-cloud-sdk-core\lib\request-wrapper.js:224:21)
    at C:xxxxxxxxxxxxxxx\node_modules\ibm-cloud-sdk-core\lib\request-wrapper.js:212:25
    at process._tickCallback (internal/process/next_tick.js:68:7)
  message: 'connect ETIMEDOUT 23.41.93.168:443',
  statusText: 'ETIMEDOUT',
  body:
   'Response not received - no connection was made to the service.' }
```
これでAPIを実行する環境ができました。

#対処方法
node-sdkのところに書いてありました。:open_hands:
[Use behind a corporate proxy](https://github.com/watson-developer-cloud/node-sdk#use-behind-a-corporate-proxy)
パッケージの「[tunnel](https://github.com/koichik/node-tunnel/)」を使ってね、とのこと
早速インストール

```
npm install tunnel
```
そしてgithubのExampleをみつつ

```javascript:collection.js
const DiscoveryV1 = require("ibm-watson/discovery/v1");
const { IamAuthenticator } = require("ibm-watson/auth");
const tunnel = require("tunnel"); // https Agent

const httpsAgent = tunnel.httpsOverHttp({
  proxy: {
    host: "xxxxxx.com", //httpとかいらない
    port: 8080,
  },
});

const discovery = new DiscoveryV1({
  version: "2019-04-30",
  authenticator: new IamAuthenticator({
    apikey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    httpsAgent,
    proxy: false, 
  }),
  httpsAgent,
  proxy: false, 
  serviceUrl:
    "https://xxxxxxxxxxxxxx",
});

const listCollectionsParams = {
  environmentId: "xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
};

discovery
  .listCollections(listCollectionsParams)
  .then((listCollectionsResponse) => {
    console.log(JSON.stringify(listCollectionsResponse, null, 2));
  })
  .catch((err) => {
    console.log("error:", err);
  });
```
そして実行すると・・・
※idなどの情報はマスクしました

```
{
  "status": 200,
  "statusText": "OK",
  "headers": {
    "content-type": "application/json;charset=utf-8",
    "access-control-allow-origin": "*",
    "access-control-allow-methods": "GET, PUT, POST, DELETE, OPTIONS",
    "cache-control": "no-cache, no-store, no-transform, max-age=0",
    "pragma": "no-cache",
    "expires": "0",
    "x-content-type-options": "nosniff",
    "x-xss-protection": "1",
    "content-security-policy": "default-src 'none'",
    "access-control-allow-headers": "Origin, Content-Type, Content-Length, Accept, X-Watson-Authorization-Token, X-WDC-PL-OPT-OUT, X-Watson-UserInfo, X-Watson-Learning-Opt-Out, X-Watson-Metadata",
    "strict-transport-security": "max-age=31536000; includeSubDomains;",
    "x-global-transaction-id": "4085110c3d0bc216af495b254da2ab8b",
    "x-dp-watson-tran-id": "4085110c3d0bc216af495b254da2ab8b",
    "content-length": "340",
    "x-edgeconnect-midmile-rtt": "165",
    "x-edgeconnect-origin-mex-latency": "222",
    "date": "Wed, 09 Sep 2020 13:27:25 GMT",
    "connection": "close"
  },
  "result": {
    "collections": [
      {
        "collection_id": "xxxxxxxxxxxxxxxxxxxx",
        "name": "test",
        "configuration_id": "xxxxxxxxxxxxxxxxxxxxx",
        "language": "ja",
        "status": "active",
        "description": null,
        "created": "2020-09-09T12:55:36.283Z",
        "updated": "2020-09-09T12:55:36.283Z"
      }
    ]
  }
}
```

めでたしめでたし:clap::clap:

#終わりに
私が困っているようなことなんて、とっくにほかのだれかが困っていて解決策を出してくれているのできちんと公式のドキュメントをしっかり見よう!!
英語翻訳バンザイ:point_up:

###DiscoveryのQueries>Query a collectionではまったとこ
クエリーの返り値フィールドを指定できるパラメーターの書き方がややこしくてはまった。
複数なのでリストかと思っていたら全然違かった。
正しくは下記

```javascript
const queryParams = {
  environmentId: '{environment_id}',
  collectionId: '{collection_id}',
  _return:'id,metadata.name'　//ここ
};
```