IBM Cloud FunctionsでNode.jsのパッケージを利用してみた

# はじめに
この記事は、LINEとIBM Cloud Functionsを連携したSlackへの通知機能を作ったので、3回に分けて紹介したい記事の第2回になります。
　第1回：[IBM Cloud Functons 動かしてみた](https://qiita.com/katahiro/items/4b515c12e42e98e17eb7)
　第2回：[IBM Cloud FunctionsでNode.jsのパッケージを利用してみた](https://qiita.com/katahiro/items/1d483030527367338574) ← この記事
　第3回：[IBM Cloud Functions でLINEとSlack連携させてみた](https://qiita.com/katahiro/items/298eac9c0fa875333d84)
今回は、アクションにNode.jsのパッケージを利用して、より自由な処理を作れることを紹介したいと思います。

# 用意するもの
- Node.js のプログラムの開発環境(筆者は `VSCode` を利用します)
- [IBM Cloud CLI](https://cloud.ibm.com/docs/cli?topic=cli-install-ibmcloud-cli&locale=ja)
- [Functions のCLIプラグイン](https://cloud.ibm.com/docs/openwhisk?topic=openwhisk-cli_install)

# 注意
今回ご提示する方法は、ローカルPCでデバッグできるようなやり方ではなく、都度IBM Cloud Functionsにアクションを登録(更新)して動作を確認するものになります。

# 注意２
普通の反応「せっかくブラウザ上でコード編集できるのに、ローカルPCでコード書かないとあかんの？」
IBM Cloud「せやねん。すまんな。。。」

例えば、Functionsで利用可能なNode.jsのパッケージを[リンク先](https://cloud.ibm.com/docs/openwhisk?topic=openwhisk-runtimes)で公開されていますが、ブラウザで編集可能なアクションのコードにパッケージを記述しても利用できなくて、ローカルPCで作成したコードと、利用したいパッケージを宣言した `package.json` をFunctionsにアクションとして登録する必要があります。

# コーディング
さて、紹介なので、簡易なコードにしましょう
## package.json
`request` パッケージを利用したいので、`dependencies` に登録しています

```Javascript:package.json
{
  "name": "qiita-action",
  "version": "1.0.0",
  "main": "action.js",
  "dependencies": {
    "request": "^2.88.0"
  }
}
```

## action.js
どんな紹介コードが良いかと考えましたが、前回に公開したAPIにアクセスするようにしましょう。

```Javascript:action.js
// 実行するメソッド
const main = async ( params ) => {
  // 前回に公開したAPIを設定、params に name があれば、それを設定しています
  const apiUrl = 'https://81fe65f5.jp-tok.apigw.appdomain.cloud/function-api/helloworld';
  const callUrl = params.name ? `${apiUrl}?name=${params.name}` : apiUrl;
  // HTTPリクエストのオプションを指定します
  const getHttpRequestOption = {
    method : "GET",
    url : callUrl,
    headers : {
      "Content-Type": "application/json",
    }
  }
  const response = await callAPI(getHttpRequestOption);
  const bodyJson = JSON.parse(response.body); // bodyのJSONがescapeされているので、JSONに戻します
  return bodyJson;
}

// requestのパッケージを利用する処理を外出ししています
const callAPI = (httpOption) => {
  return new Promise((resolve,reject) => {
    const request = require( 'request' );
    request( httpOption, ( err, res, buf ) => {
      if( err ){
        reject( { status: false, error: err } );
      }else{
        resolve(res);
      }
    });
  })
}
// IBM Cloud Functions では、 `main` メソッドが外部から呼び出されるので、exportsが必須です
exports.main = main;
```

# アクションの登録
## IBM Cloudへのログインと環境の確認
とりあえず、CLIでIBM Cloudにログインしましょう。今回は東京リージョン( `jp-tok` )を選択しています。

```DOS:コマンドプロンプト
# ログイン
ibmcloud login -u [登録アドレス] -p [登録パスワード]
# リージョンの指定
ibmcloud target -r jp-tok
# Functionsのプラグインが導入されているか確認
ibmcloud plugin list | findstr function
  cloud-functions/wsk/functions/fn       1.0.44
```

## コードをZIPに固める
今回作成した `package.json` と `action.js` をZIPファイルにする必要があります。
作業ディレクトリに両ファイルがある前提で、以下のコマンドでZIPファイルを作成します。`qiita-action.zip`が出力されるはずです。

```DOS:Windows
powershell Compress-Archive -Path action.js,package.json -DestinationPath qiita-action.zip -Force
```
```console:Mac/Linux
zip -r qiita-action.zip action.js package.json
```

## アクションをCLIで登録
初回(新規登録)と2回目以降(更新)でコマンドの指定が変わるので注意してください。正常に登録できると `ok` が表示されます。

```
# 初回アクション作成時
ibmcloud fn action create qiita-action qiita-action.zip --kind nodejs:10
  ok: created action qiita-action

# 2回目以降（更新時）
ibmcloud fn action update qiita-action qiita-action.zip --kind nodejs:10
  ok: updated action qiita-action
```
## ブラウザで確認
無事に、アクションが登録されていますね。今回はアクションを何かしらのFunctionsのパッケージに紐づけているわけではないので、`デフォルト・パッケージ` の部分に追加されているはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651598/4f97e996-5e3f-3ff2-68a3-fbab03dacbe6.png)

## 起動してみよう
`起動` をクリックすると、しばらく時間がかかってアクティベーションの欄に、前回のAPIを呼び出した結果が取得できていることがわかります。
吹き出しに記載しましたが、ZIPでアクションを登録すると、コード自体をブラウザで編集することはできなくなってしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651598/8f6c442d-7057-cce1-a3c5-a0f210fd18f6.png)

## パラメータも指定してみよう
前回操作したように、`パラメータを付けて起動`をクリックして、`name`のパラメータを指定してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651598/285b0079-173c-9559-3b95-b69431f99fb6.png)
起動してみると、無事にパラメータを前回公開したAPIに渡せているようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651598/cc69de04-1432-e420-c70d-c3ffcef06da7.png)

# 作成したアクションをAPIで公開
基本的に操作は、前回と同じです。
Functionsのトップページから `API` を画面左部から選択して、前回作成したAPIが表示されるので、そちらをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651598/74dcaf57-22eb-77fc-1603-cf08e80eac6e.png)
次に、画面左部の`定義および保護`を選択し、`操作の作成`を選択して、新しいAPIを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651598/fed8ddee-6098-050b-6a38-2556b3d25a29.png)
今回のAPIは`POST`でリクエストを受取るようにしてみましょう。
`アクションを含むパッケージ`では、今回アクションが登録されている`デフォルト`を選択し、`アクション`の部分で、今回登録したアクションを選択していることを確認しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651598/5ec3f443-77c7-2d1d-3112-e82d43c1bf44.png)
操作を作成したら、元の画面下部で`保存`を忘れずにクリックして更新してください。

# 公開されたAPIの確認
ブラウザで、今回登録したAPIを呼び出してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651598/83ec840d-40b9-3684-31bb-f7c306622028.png)
今回は`POST`でリクエストを処理する設定にしているので、ブラウザにURLを貼り付けるだけの`GET`では処理されないことが確認できました。想定通りですね。

では、`VSCode`の拡張機能`REST Client`を利用して `POST` で呼び出してみましょう。
無事にレスポンスで、前回公開したAPIの応答結果を受取れていることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651598/d386d9d6-5e0f-ce46-1605-ba3824589579.png)

# さいごに
いかがでしたでしょうか？Node.jsの`request`のパッケージを利用して、呼び出されたAPIが別のAPIを呼び出して、その結果を応答するということが出来ました。
つまり、IBM Cloudに関わらず、世の中のAPIを利用したIBM Cloud Functionsのアクションを作成する準備が完了したということです。
次回は、今回のコードをベースに、`LINE`や`Slack`のAPIを利用して、LINEに投稿された内容をSlackに通知する機能の実装について紹介したいと思います。
　第1回：[IBM Cloud Functons 動かしてみた](https://qiita.com/katahiro/items/4b515c12e42e98e17eb7)
　第2回：[IBM Cloud FunctionsでNode.jsのパッケージを利用してみた](https://qiita.com/katahiro/items/1d483030527367338574)
　第3回：[IBM Cloud Functions でLINEとSlack連携させてみた](https://qiita.com/katahiro/items/298eac9c0fa875333d84)