[IBM API Connect] クエリパラメータを使って動的にAPIレスポンスの項目を制御する方法

# やりたいこと
複数の要素を返すバックエンドAPIの応答から、クライアントの指定した要素のみを抽出して返すAPIゲートウェイを構成する方法を考えてみます。例えば、`GET /accounts/1001?mask=name,age`のように、変数(ここでは"mask")に項目名を指定することで、クライアント側で必要な項目だけを返すAPIを作ってみます。

```json:GET_/accounts/1001
{
  "id" : "1001",
  "name" : "alice",
  "phone" : "000-000-0000",
  "gender" : "female", 
  "age" : 20
}
```

つまり、:arrow_up:これを:arrow_down:こうしたい。


```json:GET_/accounts/1001?mask=name,age
{
   "name" : "alice",
   "age" : 20
}
```
# 前提、環境等
- 2017/07/26 時点の IBM API Connect on Bluemix Public で動作を確認しています

# 実装
## 実装方針
IBM API Connectには様々なメッセージ変換部品が用意されていますが、今回は![GatewayScript](https://qiita-image-store.s3.amazonaws.com/0/129138/ebfb51d0-3d6c-4231-1394-3a8a182948f2.png)ポリシーを使ってみます。GatewayScriptポリシーの詳細は[マニュアル][kc-gs]を確認してください。

## 処理の流れ
処理の流れはこんな感じになります。
1. リクエスト・パラメートして`mask`を受け付ける
2. `Invoke` ポリシーでバックエンドAPIからアカウント情報のJSONを取得
3. `GatewayScript` ポリシーで`mask`の内容に応じてアカウント情報をマスキング
  3.1 `Invoke`の結果を変数に読み込む
  3.2 `mask`パラメータをカンマで分割して変数に読み込む
  3.3 `Invoke`の結果から、`mask`で指定された項目のみをコピーしたオブジェクトを組み立て
  3.4 組み立てたオブジェクトを応答に書き出し

## アセンブリー・フロー実装
フローの全体像はこんな感じになります。
![image](https://qiita-image-store.s3.amazonaws.com/0/129138/1a11f7b1-9295-909e-2413-8a6c0a113fb8.png)

invoke部分は適当なバックエンドを呼ぶように構成してください。また今回のサンプルではResponse Object Variableが指定されていない前提でmask-outputが書かれていますので、空であることを確認します。
![image](https://qiita-image-store.s3.amazonaws.com/0/129138/6949a97b-7f77-c229-25a3-8d93bc07de75.png)

## GatewayScript実装
今回の主役である`mask-output`のコードはこんな感じです。

```javascript:mask-output
apim.readInputAsJSON(function (error,inObj) {  
  // 前ステップ(invoke)の出力をinObjに読み込む
  if (error) {
    console.log("Failed to read data in JSON format");
  } else {
    var maskStr = apim.getvariable('request.parameters.mask');
    if (maskStr) {  
      // maskパラメータが存在する場合のみ、マスキングを実行
      var maskAry = maskStr.split(',');
      var outObj = {};

      for(let mask of maskAry){  
        // maskに指定された項目をoutObjにコピー
        if(inObj.hasOwnProperty(mask)){
          outObj[mask] = inObj[mask];
        }
      }

      // outObjの書き出し処理
      session.output.write(outObj);
      apim.output('application/json');
    }
  }
});
```
## 実行結果
API Managerのテストツールからテストしてみます。
### maskパラメータを指定しない場合
![image](https://qiita-image-store.s3.amazonaws.com/0/129138/6a8f91a5-2766-ac8a-be89-5660422b78a9.png)

### mask=name,ageと指定した場合
![image](https://qiita-image-store.s3.amazonaws.com/0/129138/01105d24-db02-d1db-ea37-aca486cb3462.png)
無事に指定した項目で応答がマスクされました。

# コード
今回作成したAPI定義はこちらです。`target-url: "set-your-url-here"`を適当な値に置き換えてご利用ください。

```yaml:accounts-masked_1.0.0.yaml
---
swagger: "2.0"
info:
  x-ibm-name: "accounts-masked"
  title: "accounts-masked"
  version: "1.0.0"
schemes:
- "https"
host: "$(catalog.host)"
basePath: "/accounts-masked"
consumes:
- "application/json"
produces:
- "application/json"
securityDefinitions:
  clientIdHeader:
    type: "apiKey"
    in: "header"
    name: "X-IBM-Client-Id"
security:
- clientIdHeader: []
x-ibm-configuration:
  testable: true
  enforced: true
  cors:
    enabled: true
  assembly:
    execute:
    - invoke:
        target-url: "set-your-url-here"
    - gatewayscript:
        title: "mask-output"
        version: "1.0.0"
        source: "apim.readInputAsJSON(function (error,inObj) {  // 前ステップ(invoke)の出\
          力をinObjに読み込む\r\n  if (error) {\r\n    console.log(\"Failed to read data\
          \ in JSON format\");\r\n  } else {\r\n    var maskStr = apim.getvariable('request.parameters.mask');\r\
          \n    if (maskStr) {  // maskパラメータが存在する場合のみ、マスキングを実行\r\n      var maskAry\
          \ = maskStr.split(',');\r\n      var outObj = {};\r\n\r\n      for(let mask\
          \ of maskAry){  // maskに指定された項目をoutObjにコピー\r\n        if(inObj.hasOwnProperty(mask)){\r\
          \n          outObj[mask] = inObj[mask];\r\n        }\r\n      }\r\n\r\n\
          \      // outObjの書き出し処理\r\n      session.output.write(outObj);\r\n     \
          \ apim.output('application/json');\r\n    }\r\n  }\r\n});"
  phase: "realized"
paths:
  /accounts/{id}:
    get:
      responses:
        200:
          description: "200 OK"
      parameters:
      - name: "mask"
        type: "string"
        required: false
        in: "query"
        description: "Keys to be passed to client, in comma  separated value format."
    parameters:
    - name: "id"
      type: "string"
      required: true
      in: "path"
      description: "user id"
definitions:
  userInfo:
    description: ""
    type: "object"
    properties:
      id:
        type: "string"
      name:
        type: "string"
      phone:
        type: "string"
      gender:
        type: "string"
      age:
        type: "number"
    example: "{\r\n\"id\": \"1001\",\r\n\"name\": \"alice\",\r\n\"phone\": \"000-000-0000\"\
      ,\r\n\"gender\": \"female\",\r\n\"age\": 20\r\n}"
    additionalProperties: true
tags: []

```


# 参考情報

[IBM Knowledge Center - GatewayScript][kc-gs]

[kc-gs]:https://www.ibm.com/support/knowledgecenter/en/SSMNED_5.0.0/com.ibm.apic.toolkit.doc/rapim_ref_ootb_policygatewayscript.html