IBM API Connect 関連メモ - (8)z/OS Connect 連携におけるインターフェースの浄化

# はじめに
今回もAPI Connect - z/OS Connect連携を想定します。
z/OS Connectは既存のz/OS上の資産(CICSアプリケーションやIMSトランザクションなど)をサービスとして外部に公開する仕組みを提供しています。基本的には既存資産がベースになるため、既存のサービスのインターフェースをそのまま公開すると使い勝手が良くない場合があります。例えば、フィールドの名前や構造が分かりにくいといった場合です。
API Connect, z/OS Connectではそれぞれ既存のサービスのAPIを公開/管理するための仕組みを提供していますが、その際に既存インターフェースをなるべく使いやすい形に変えて公開することが望まれます(ここでは理想のインターフェースに変換することを"浄化"と表現します)。

z/OS Connect, API Connectそれぞれでインターフェースを浄化するためにどのようなことができるのかを具体的に見ていきます。


## 関連記事

[IBM API Connect 関連メモ - (1)既存REST APIの取り込み](https://qiita.com/tomotagwork/private/d02f8e6092740b0e782f)
[IBM API Connect 関連メモ - (2)API Connectツールキットを使用したNode.jsアプリ作成(Loopback)](https://qiita.com/tomotagwork/private/7872ec8f99de57d7819a)
[IBM API Connect 関連メモ - (3)カタログ、開発者ポータルの構成](https://qiita.com/tomotagwork/private/da577551d7f4c50c4fd2)
[IBM API Connect 関連メモ - (4)各種APIの管理](https://qiita.com/tomotagwork/private/99e9b35227a0f80b2a91)
[IBM API Connect 関連メモ - (5)Secure Gateway経由でのz/OS Connectとの連携](https://qiita.com/tomotagwork/private/59e88b782bb72f1d0c1a)
[IBM API Connect 関連メモ - (6)API Connect-z/OS Connect間TLS接続](https://qiita.com/tomotagwork/private/c70b8566129e51c92423)
[IBM API Connect 関連メモ - (7)z/OS Connect 基本認証 + ID伝播](https://qiita.com/tomotagwork/private/38d41cbc01f92352f649)
[IBM API Connect 関連メモ - (8)z/OS Connect 連携におけるインターフェースの浄化](https://qiita.com/tomotagwork/private/0ec8d2224ad141756510)
[IBM API Connect 関連メモ - (9)z/OS Connect 連携まとめ](https://qiita.com/tomotagwork/private/a9fae53f02773a0c67b1)

# 全体像

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/223c5ddc-9014-9af0-34b8-916b7aca8929.png)

基本的には上のような流れで既存のI/Fが公開用のI/Fに変換されていくことになります。

まず、公開したい既存のアプリケーション(ここではCICS-COBOLのCOMMAREAアプリケーション)が切り出されたとしましょう。COMMAREAアプリケーションはその名前の通り"COMMAREA"と呼ばれるデータエリアでデータの受け渡しを行うため、入出力データの構造はCOMMAREAの構造を示すCopyBookとして定義されます。
z/OS Connectでは、ターゲットのプログラムの入出力データ構造体を示すCopyBookを取り込んでそれをJSONフォーマットとの相互変換を行う機能を提供してくれます。つまり、CopyBookの定義に従って、それにに相当するJSON Schemaを自動生成してくれます。
ここで、元になるCopyBookというのは当然COBOLの世界で定義されたものですので、外部接続やRESTやJSONの考え方を意識した定義にはなっていないことが多々あります。例えばフィールドのネーミングや構造についてはローカルでCOBOLの世界で実施する分には問題ないかもしれませんが、外部公開して汎用的に使われることを想定すると望ましくない定義になっている可能性があります。そこで、公開していく過程でI/Fの浄化をしていくことになりますが、I/Fに対して各フェーズでどのような操作ができるかはそれぞれ違っています。


以下細かく見ていきます。

# 1.COBOLレベルでの変換
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/a8402de5-3896-b51d-c0c8-3f2f76a5012b.png)

Copybookをz/OS Connectのツールに取り込む際、上に上げたように既存I/FとしてのCopyBookの定義内容がそのまま公開するには望ましくない場合があります。そこで、対象のCopyBookを**ツールに取り込む前に**ある程度都合のよいように変更してしまうということが考えられます。

COBOLの構造体定義はJSONやXMLと違ってメモリ上に確保さえれるエリアを意識した定義方法となっています。すなわち各フィールドは開始位置からのオフセットと長さが重要な要素となっています。例えば以下のような顧客情報を表すCopyBookを考えてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/3cb6118c-1d1c-16c1-e874-5ffbd39b9a33.png)

ターゲットとなるアプリケーションのCOMMAREA構造体が上の図左側のCOMMAREA01のように定義されていたとします。
これをそのままツールに取り込むと、想定されるJSONデータは以下のようなフィールド名、構造になります。

```json:
{ 
    "COMMAREA01": {
        "CUST-ID":"A0001",
        "CUST-NAME": "Taro Yamada",
        "ADDRESSLINE": "Mihama-ku           Nakase 1-1",
        "CITY":"Chiba-shi",
        "STATE":"Chiba-ken",
        "ZIP":"111-2222",
        "COUNTRY":"Japan",
        "PHONE":"050-1111-2222"
    }
}
```



これをフィールド名や階層を変えて上の図右側のCOMMAREA02のように定義し直してみます。このように、実体であるメモリ上展開されるデータ構造に影響を与えない範囲でCopyBook上の定義を変更することが可能です(addressLineの部分は1つのフィールドを前後20バイトずつに分割しているのでそれでアプリ上問題ないことが前提ですが)。
このように変更したCopyBookをツールに取り込むと、想定されるJSONデータは以下のようなフィールド名、構造になります。

```json:
{ 
    "COMMAREA02": {
        "customerID":"A0001",
        "customerName": "Taro Yamada",
        "address": {
            "addressLine1":"Mihama-ku",
            "addressLine2":"Nakase 1-1",
            "city":"Chiba-shi",
            "state":"Chiba-ken",
            "zip":"111-2222",
            "country":"Japan"
        },
        "phone":"050-1111-2222"
    }
}
```

このように、ツールで変換される形態を想定して、取り込むCopyBookを事前に都合の良いように変換しておくことが考えられます。
ここで行えるのは、フィールド名の変更、限られた範囲での階層構造の変更の辺りです。あとはアプリケーションの作り次第ですが、フィールドの結合や分割が行えるケースもあるかと思います。
また、この変換を行うためのツールが用意されている訳ではありませんのでこの部分は作りこみが必要になりますが、ここは純粋な文字列操作を行うだけですので、スクリプトで比較的容易に実装可能と考えられます。

# 2.z/OS Connectレベルでの変換(API toolkit)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/246d9a65-85ba-5a33-4964-3e5d4d8cd0cb.png)

z/OS Connect提供のAPI Toolkitでは、ターゲットアプリケーションのI/FであるCOMMAREA構造体のCopyBookを元にREST API化することができますが、ここでは2段階のI/Fの変換が行われます。

## 2-1. Copybookの取り込み (CopyBook => 個別サービス定義)
まず、CopyBookを取り込んで"サービス"として認識させるフェーズがあります。
例えば、カタログから商品の在庫確認をしたり、オーダーするサービスを提供することを想定すると、まず在庫確認用のサービスや、オーダー用のサービスをそれぞれ定義していくことになります。

ここでは、z/OS Connect EE サービス・プロジェクト というプロジェクトを作成し、既存I/Fの入出力のデータ構造をCopyBookから読み取り、サービス・インターフェース・エディターで編集します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/8048f489-a256-35c5-fe22-e71a932156c5.png)

ここではフィールド毎に以下のような操作ができます。

- フィールド名の変更
- I/Fとして公開するかどうかの選択
- デフォルト値の設定
- コード変換有無

データの階層構造は入力とするCopyBookに従うので、この段階で階層構造をカスタマイズすることはできません。

## 2-2. RestfulService作成 (個別サービス定義 => RESTful API)
2-1の段階ではHTTPでサービス呼び出しを行える状態になりますが、これだといわゆるRPCベースの呼び出しが行えるようになっただけで、RESTful APIとは呼べません。

在庫確認のサービスや、オーダーのサービスなど、個々のサービス定義を行ったら、次にそれら関連する操作をRESTfulサービスとしてまとめるフェーズがあります。
ここでは、z/OS Connect EE API プロジェクトというプロジェクトを作成し、2-1で作成した個々のサービスをまとめてRESTfulサービスとして定義していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/e30b3aab-c4b6-864d-2b99-d52fdc7c2d65.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/bb329628-95b8-4e87-d3b6-2c3236348e03.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/f4bc834f-fc28-41a6-5980-c23653e5267d.png)

ここでは、個々のサービス呼び出しに対して、以下のような操作が行えます。

- 個々のサービス呼び出しに対してURIパスとHTTPメソッド(GET/POST/PUT/DELETE/PATCH/HEAD)の割り当て(**※1**)
- HTTPレスポンスコード毎のI/Fマッピング (**※2**)
- 要求、応答(HTTPレスポンス毎)のI/Fマッピング
    - 固定値のアサイン
    - I/Fとして公開するかどうかの選択
    - URIパス、queryString、HTTPヘッダーとJSONデータへのマッピング(**※3**)

補足
**※1**
商品一覧紹介のリクエストであればGETメソッドで/listというURIパスを指定した場合に商品一覧用のサービスを呼び出す、オーダーのリクエストであればPOSTメソッドで/placeOrderというURIパスを指定してオーダー内容をBodyにJSONデータを指定しオーダー用のサービスを呼び出す、といったように、サービス用途に合わせてURIパス、メソッドを定義します。

**※2**
一般的にはHTTPレスポンスコード(200: 正常応答、500: Internal Server Errorなど)によって返されるメッセージのデータ構造が異なることになりますので、レスポンスコードに応じてそれぞれ構造をどのように公開するかを定義します。
1つのレスポンスコードに対して複数の応答用データ構造のパターンが存在する場合はAPI Toolkitでは対応できません。例えば、会員IDをキーに会員情報紹介をするためのサービスがあった場合、通常会員の場合の結果と特別会員の場合の結果のデータ構造が全く違うようなケースの場合は対応できません(データ構造は共通化できて一方だけ特定のフィールドがブランクになる、といったような場合はOKですが)。応答用のデータ構造が複数パターンがある場合は別のサービスとして定義する、ラッパーのプログラムをかませてI/Fの共通化をはかる、などの対応が必要になります。

**※3**
CopyBookを取り込んで入出力データ構造を認識させると、基本、それらは全てMessage Bodyに含まれるJSONデータとして取り扱われることになります。
しかし、REST APIでは、入力データはBody部分のJSONだけでなく、URIパス、QueryString、HTTPヘッダーとしても与えることができます。APIの設計として変数(パラメーター)をどのように与えるかは選択の余地があります。そこで、URIパス、QueryString、HTTPヘッダーの情報をMessage Bodyのフィールドにマッピングするということもできるようになっています(URIパスとQueryStringは要求時のみ)。
例えば以下のようなイメージです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/d0d0ee04-6380-26f3-2ec0-3c9ddc85c679.png)

z/OS ConnectではこのようなI/Fの変換を行って公開用のI/Fを定義していき、最終的にSwagger文書を生成させることができます。

# 3. API Connectレベルでの変換(アセンブル)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/e58d15a6-4c4a-b112-dd67-ecde3761bf0c.png)

z/OS Connectでは上で見てきた通り既存のz/OS上のサービスをRESTful API化するための支援機能が提供されています。上で挙げたカスタマイズで足りるケースもあるとは思いますが、そこで行えるインターフェースの浄化のためのカスタマイズは限定的です。
API ConnectではAPI DesignerのUIを用いて、APIが呼び出された時に実行される処理を組み込むことができます("アセンブル"と呼ばれます)。
以下のようなブラウザのインターフェースでGUIベースで各種ポリシー、および、ロジック構成体と呼ばれるオブジェクトを組み合わせて様々な処理を行わせることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/f3e2d01e-2f0d-6f51-f0cc-1462f2682d75.png)
参考: [API のポリシーおよびロジック構成体](https://www.ibm.com/support/knowledgecenter/ja/SSMNED_5.0.0/com.ibm.apic.toolkit.doc/capim_policies.html)
これを利用すれば様々な処理を柔軟に組み合わせることができますので、ドラスティックに入出力データ構造を変更してそのマッピングを定義したり、複数のAPIを組み合わせるなど、かなり柔軟にカスタマイズを行うことができます。
ここで定義された"アセンブル"は、最終的にはyamlファイルに落とし込まれ、ソースタブで確認することができます。

ここでは、単純ケースとして、z/OS Connectで出力したSwagger文書(外部公開用I/F①)を取り込んだのち、新たな外部公開用I/F②を定義しなおして、その変換をアセンブルで実装する流れを具体的に試してみたいと思います。

z/OS Connectで公開されたSwagger文書をAPI Connectに取り込む手順は以下で実施済みなのでここでは割愛します。
[API Connect からSecure Gateway経由でのバックエンド・サービス連携](https://qiita.com/tomotagwork/private/ea03c9eea31878efab47#api-connect-%E3%81%8B%E3%82%89secure-gateway%E7%B5%8C%E7%94%B1%E3%81%A7%E3%81%AE%E3%83%90%E3%83%83%E3%82%AF%E3%82%A8%E3%83%B3%E3%83%89%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E9%80%A3%E6%90%BA)

## カスタマイズ前のI/F確認
まず、前提としてz/OS Connectで生成した以下のSwagger文書を取り込んだとします。

<details><summary>公開用I/F①</summary><div>

```json:swagger.json
{
  "swagger" : "2.0",
  "info" : {
    "description" : "",
    "version" : "1.0.0",
    "title" : "cicsstockrestfulservice"
  },
  "host" : "localhost:8080",
  "basePath" : "/showroomdemo/v1/isecics",
  "schemes" : [ "https", "http" ],
  "consumes" : [ "application/json" ],
  "produces" : [ "application/json" ],
  "paths" : {
    "/order" : {
      "post" : {
        "tags" : [ "cicsstockrestfulservice" ],
        "operationId" : "postCICSStockOrderService",
        "parameters" : [ {
          "in" : "body",
          "name" : "postCICSStockOrderService_request",
          "description" : "request body",
          "required" : true,
          "schema" : {
            "$ref" : "#/definitions/postCICSStockOrderService_request"
          }
        } ],
        "responses" : {
          "400" : {
            "description" : "Bad Request",
            "schema" : {
              "$ref" : "#/definitions/postCICSStockOrderService_response_400"
            }
          },
          "200" : {
            "description" : "OK",
            "schema" : {
              "$ref" : "#/definitions/postCICSStockOrderService_response_200"
            }
          }
        }
      }
    },
    "/stock/{groupCode}" : {
      "get" : {
        "tags" : [ "cicsstockrestfulservice" ],
        "operationId" : "getCICSStockCheckService",
        "parameters" : [ {
          "name" : "groupCode",
          "in" : "path",
          "required" : true,
          "type" : "string",
          "maxLength" : 4
        } ],
        "responses" : {
          "400" : {
            "description" : "Bad Request",
            "schema" : {
              "$ref" : "#/definitions/getCICSStockCheckService_response_400"
            }
          },
          "200" : {
            "description" : "OK",
            "schema" : {
              "$ref" : "#/definitions/getCICSStockCheckService_response_200"
            }
          }
        }
      }
    }
  },
  "definitions" : {
    "getCICSStockCheckService_response_400" : {
      "type" : "object",
      "properties" : {
        "stockCheck" : {
          "type" : "object",
          "properties" : {
            "returnCode" : {
              "type" : "integer",
              "minimum" : 0,
              "maximum" : 99
            },
            "responseMessage" : {
              "type" : "string",
              "maxLength" : 80
            }
          }
        }
      }
    },
    "getCICSStockCheckService_response_200" : {
      "type" : "object",
      "properties" : {
        "stockCheck" : {
          "type" : "object",
          "properties" : {
            "returnCode" : {
              "type" : "integer",
              "minimum" : 0,
              "maximum" : 99
            },
            "responseMessage" : {
              "type" : "string",
              "maxLength" : 80
            },
            "itemList" : {
              "type" : "array",
              "items" : {
                "type" : "object",
                "properties" : {
                  "itemId" : {
                    "type" : "string",
                    "maxLength" : 8
                  },
                  "itemName" : {
                    "type" : "string",
                    "maxLength" : 66
                  },
                  "itemPrice" : {
                    "type" : "integer",
                    "minimum" : 0,
                    "maximum" : 9999999
                  },
                  "stored" : {
                    "type" : "string",
                    "maxLength" : 1
                  },
                  "shipFrom" : {
                    "type" : "string",
                    "maxLength" : 64
                  }
                }
              },
              "maxItems" : 6,
              "minItems" : 6
            }
          }
        }
      }
    },
    "postCICSStockOrderService_request" : {
      "type" : "object",
      "properties" : {
        "stockOrder" : {
          "type" : "object",
          "properties" : {
            "orderList" : {
              "type" : "array",
              "items" : {
                "type" : "object",
                "properties" : {
                  "itemId" : {
                    "type" : "string",
                    "maxLength" : 8
                  },
                  "itemQuantity" : {
                    "type" : "integer",
                    "minimum" : 0,
                    "maximum" : 9999999
                  }
                }
              },
              "maxItems" : 6,
              "minItems" : 6
            }
          }
        }
      }
    },
    "postCICSStockOrderService_response_400" : {
      "type" : "object",
      "properties" : {
        "stockOrder" : {
          "type" : "object",
          "properties" : {
            "returnCode" : {
              "type" : "integer",
              "minimum" : 0,
              "maximum" : 99
            },
            "responseMessage" : {
              "type" : "string",
              "maxLength" : 80
            }
          }
        }
      }
    },
    "postCICSStockOrderService_response_200" : {
      "type" : "object",
      "properties" : {
        "stockOrder" : {
          "type" : "object",
          "properties" : {
            "returnCode" : {
              "type" : "integer",
              "minimum" : 0,
              "maximum" : 99
            },
            "responseMessage" : {
              "type" : "string",
              "maxLength" : 80
            },
            "stockOfItemList" : {
              "type" : "array",
              "items" : {
                "type" : "object",
                "properties" : {
                  "itemId" : {
                    "type" : "string",
                    "maxLength" : 8
                  },
                  "stockYN" : {
                    "type" : "string",
                    "maxLength" : 1
                  },
                  "stockNum" : {
                    "type" : "integer",
                    "minimum" : 0,
                    "maximum" : 9999999
                  }
                }
              },
              "maxItems" : 6,
              "minItems" : 6
            }
          }
        }
      }
    }
  }
}
```

</div></details>

これを取り込んでデータ構造はそのままに単純なアセンブルを作成し、TLSサーバー認証 + Basic認証の構成を行っているものとします(手順は以下の通り)。
[API Connect からSecure Gateway経由でのバックエンド・サービス連携](https://qiita.com/tomotagwork/private/ea03c9eea31878efab47#api-connect-%E3%81%8B%E3%82%89secure-gateway%E7%B5%8C%E7%94%B1%E3%81%A7%E3%81%AE%E3%83%90%E3%83%83%E3%82%AF%E3%82%A8%E3%83%B3%E3%83%89%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E9%80%A3%E6%90%BA)
[SAF Basic認証 + TLSサーバー認証(HTTPS)](https://qiita.com/tomotagwork/private/649f9c3becaeff539fea#2-saf-basic%E8%AA%8D%E8%A8%BC--tls%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E8%AA%8D%E8%A8%BChttps)

この段階でのAPI Connect上のAPI定義は以下のようになっています。

<details><summary>API Connect上のAPI定義</summary><div>

```yml
swagger: '2.0'
info:
  description: ''
  version: 1.0.0
  title: cicsstockrestfulservice
  x-ibm-name: cicsstockrestfulservice
host: 'localhost:8080'
basePath: /showroomdemo/v1/isecics
schemes:
  - https
consumes:
  - application/json
produces:
  - application/json
paths:
  /order:
    post:
      tags:
        - cicsstockrestfulservice
      operationId: postCICSStockOrderService
      parameters:
        - in: body
          name: postCICSStockOrderService_request
          description: request body
          required: true
          schema:
            $ref: '#/definitions/postCICSStockOrderService_request'
      responses:
        '200':
          description: OK
          schema:
            $ref: '#/definitions/postCICSStockOrderService_response_200'
        '400':
          description: Bad Request
          schema:
            $ref: '#/definitions/postCICSStockOrderService_response_400'
  '/stock/{groupCode}':
    get:
      tags:
        - cicsstockrestfulservice
      operationId: getCICSStockCheckService
      parameters:
        - name: groupCode
          in: path
          required: true
          type: string
          maxLength: 4
      responses:
        '200':
          description: OK
          schema:
            $ref: '#/definitions/getCICSStockCheckService_response_200'
        '400':
          description: Bad Request
          schema:
            $ref: '#/definitions/getCICSStockCheckService_response_400'
definitions:
  getCICSStockCheckService_response_400:
    type: object
    properties:
      stockCheck:
        type: object
        properties:
          returnCode:
            type: integer
            minimum: 0
            maximum: 99
          responseMessage:
            type: string
            maxLength: 80
  getCICSStockCheckService_response_200:
    type: object
    properties:
      stockCheck:
        type: object
        properties:
          returnCode:
            type: integer
            minimum: 0
            maximum: 99
          responseMessage:
            type: string
            maxLength: 80
          itemList:
            type: array
            items:
              type: object
              properties:
                itemId:
                  type: string
                  maxLength: 8
                itemName:
                  type: string
                  maxLength: 66
                itemPrice:
                  type: integer
                  minimum: 0
                  maximum: 9999999
                stored:
                  type: string
                  maxLength: 1
                shipFrom:
                  type: string
                  maxLength: 64
            maxItems: 6
            minItems: 6
  postCICSStockOrderService_request:
    type: object
    properties:
      stockOrder:
        type: object
        properties:
          orderList:
            type: array
            items:
              type: object
              properties:
                itemId:
                  type: string
                  maxLength: 8
                itemQuantity:
                  type: integer
                  minimum: 0
                  maximum: 9999999
            maxItems: 6
            minItems: 6
  postCICSStockOrderService_response_400:
    type: object
    properties:
      stockOrder:
        type: object
        properties:
          returnCode:
            type: integer
            minimum: 0
            maximum: 99
          responseMessage:
            type: string
            maxLength: 80
  postCICSStockOrderService_response_200:
    type: object
    properties:
      stockOrder:
        type: object
        properties:
          returnCode:
            type: integer
            minimum: 0
            maximum: 99
          responseMessage:
            type: string
            maxLength: 80
          stockOfItemList:
            type: array
            items:
              type: object
              properties:
                itemId:
                  type: string
                  maxLength: 8
                stockYN:
                  type: string
                  maxLength: 1
                stockNum:
                  type: integer
                  minimum: 0
                  maximum: 9999999
            maxItems: 6
            minItems: 6
x-ibm-configuration:
  enforced: true
  testable: true
  phase: realized
  cors:
    enabled: true
  assembly:
    execute:
      - operation-switch:
          title: operation-switch
          case:
            - operations:
                - getCICSStockCheckService
              execute:
                - invoke:
                    title: invoke-stockCheck
                    timeout: 60
                    verb: GET
                    cache-response: protocol
                    cache-ttl: 900
                    stop-on-error:
                      - null
                    version: 1.0.0
                    target-url: 'https://xxxxx.securegateway.appdomain.cloud:15236/showroomdemo/v1/isecics/stock/$(request.parameters.groupCode)'
                    secure-gateway: false
                    tls-profile: zosconnecttls01
                    username: ZCON01
                    password: xxxxxxxx
            - operations:
                - postCICSStockOrderService
              execute:
                - invoke:
                    title: invoke-stockOrder
                    timeout: 60
                    verb: POST
                    cache-response: protocol
                    cache-ttl: 900
                    stop-on-error:
                      - null
                    version: 1.0.0
                    target-url: 'https://xxxxx.securegateway.appdomain.cloud:15236/showroomdemo/v1/isecics/order'
                    tls-profile: zosconnecttls01
                    username: ZCON01
                    password: xxxxxxxx
          otherwise: []
          version: 1.0.0
    catch: []
securityDefinitions:
  Client Secret:
    type: apiKey
    description: ''
    in: header
    name: X-IBM-Client-Secret
  Client ID:
    type: apiKey
    description: ''
    in: header
    name: X-IBM-Client-Id
security:
  - Client Secret: []
    Client ID: []
```

</div></details>

※z/OS Connectが生成するSwagger文書はjson形式ですが、API Connectに取り込まれるとyaml形式で保持されます。

さて、ここでカスタマイズ前の元のI/Fを確認しておきます。ここでは/orderのURIパスで呼び出されるサービスに着目してみます。

<details><summary>カスタマイズ前/order のI/F</summary><div>

```yaml:抜粋-URIパス部分
 /order:
    post:
      tags:
        - cicsstockrestfulservice
      operationId: postCICSStockOrderService
      parameters:
        - in: body
          name: postCICSStockOrderService_request
          description: request body
          required: true
          schema:
            $ref: '#/definitions/postCICSStockOrderService_request'
      responses:
        '200':
          description: OK
          schema:
            $ref: '#/definitions/postCICSStockOrderService_response_200'
        '400':
          description: Bad Request
          schema:
            $ref: '#/definitions/postCICSStockOrderService_response_400'
```

```yaml:抜粋-Requestデータ構造部分
  postCICSStockOrderService_request:
    type: object
    properties:
      stockOrder:
        type: object
        properties:
          orderList:
            type: array
            items:
              type: object
              properties:
                itemId:
                  type: string
                  maxLength: 8
                itemQuantity:
                  type: integer
                  minimum: 0
                  maximum: 9999999
            maxItems: 6
            minItems: 6
```

```yaml:抜粋-Resonse(正常応答)データ構造部分
  postCICSStockOrderService_response_200:
    type: object
    properties:
      stockOrder:
        type: object
        properties:
          returnCode:
            type: integer
            minimum: 0
            maximum: 99
          responseMessage:
            type: string
            maxLength: 80
          stockOfItemList:
            type: array
            items:
              type: object
              properties:
                itemId:
                  type: string
                  maxLength: 8
                stockYN:
                  type: string
                  maxLength: 1
                stockNum:
                  type: integer
                  minimum: 0
                  maximum: 9999999
            maxItems: 6
            minItems: 6
```

</div></details>

図にするとこんな感じのイメージです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/05cfd444-5a2f-92b9-8828-8df09cd7ea92.png)

## リクエストデータのカスタマイズ

### 公開用I/F②入力データ構造定義
まずyamlのソース画面で、postCICSStockOrderService_request の定義にならって、新たにpostCICSStockOrderService_request_externalという構造体を追加し、リクエスト用のスキーマとして指定します。

```yaml:抜粋-URIパス部分
 /order:
    post:
      tags:
        - cicsstockrestfulservice
      operationId: postCICSStockOrderService
      parameters:
        - in: body
          name: postCICSStockOrderService_request
          description: request body
          required: true
          schema:
            $ref: '#/definitions/postCICSStockOrderService_request_external'
      responses:
        '200':
          description: OK
          schema:
            $ref: '#/definitions/postCICSStockOrderService_response_200'
        '400':
          description: Bad Request
          schema:
            $ref: '#/definitions/postCICSStockOrderService_response_400'
```

```yaml:抜粋-Requestデータ構造部分
  postCICSStockOrderService_request_external:
    type: object
    properties:
      stockOrder:
        type: object
        properties:
          dummyData01Ex:
            type: string
            maxLength: 8
          testAuth:
            type: string
            maxLength: 30
          orderListEx:
            type: array
            items:
              type: object
              properties:
                itemId:
                  type: string
                  maxLength: 8
                itemQuantity:
                  type: integer
                  minimum: 0
                  maximum: 9999999
            maxItems: 6
            minItems: 6
```

ここでは、dummyData01Exというフィールドと、testAuthというフィールドを新たに追加しています。
dummyData01Exというのはその名前の通りダミーのフィールドです。ログ出力用などバックエンドには伝播させないフィールドと意味合いで追加しています。
testAuthというフィールドは、バックエンドのBasic認証用に、HTTPヘッダー "Authorization" に設定する値(userid:passwordをBase64Encodeした値)を受け渡すためのフィールドとして定義しています。実際にJSONデータにこのようにuserid,password情報を設定することは無いと思いますが、ここではアセンブルの手順/動作確認のためにこのようなマッピングを想定しています。
また、orderListは単純にorederListExという名前に変更しているだけです。実質的な意味はあまりありません。

### 入力データ構造のマッピング
新たに定義した入力データ構造(公開用I/F②)と、バックエンドアクセス用の入力データ構造(公開用I/F①)のマッピングを行います。これはアセンブル画面でmapというポリシーで実装します。以下のようなイメージでマッピング情報を定義していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/0330fd2b-c10f-9fe4-b6fe-abce3ddd29d4.png)

order用のInvokeの手前に、mapを挿入します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/9a4d83a5-0a7c-32aa-b9b5-ea2692911ba1.png)

入力の右の編集ボタンをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/c5d216d9-ad51-8728-6be4-231f9a6be03f.png)

「+入力」ボタンを押して以下のように指定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/5dfe199f-d9dd-8789-f182-70be1b01dc25.png)
コンテキスト変数: request.body
名前: 任意の名前
コンテンツ・タイプ: application/json
定義: #/definitions/postCICSStokOrderService_request_external (上で新規に定義した構造体)

前の画面で出力の右の編集ボタンをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/b5a6913d-0ab4-978f-ddb3-bfd0998a7191.png)

「+出力」ボタンを2回押して、2つの出力用の定義を以下のように指定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/051dcc63-773c-58e4-1081-8425fb4f287d.png)

＜1つめ＞
コンテキスト変数: message.body
名前: 任意の名前
コンテンツ・タイプ: application/json
定義: #/definitions/postCICSStokOrderService_request (元の入力用データ構造)


＜2つめ＞
コンテキスト変数: message.headers.Authorization
名前: 任意の名前
コンテンツ・タイプ: none
定義: string

入力、出力それぞれで指定したデータ定義が表示されるので、対応するフィールドを線でつないでマッピングを行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/fd0112a7-3a7b-2b62-b08b-256984764700.png)
ここでは、orderListEx => orderList, testAuth => HTTPヘッダー"Authorization"にマッピングしています。(dummyData01Exはどこにもマッピングせず捨てています)


## レスポンスデータのカスタマイズ

### 公開用I/F②出力データ構造定義

yamlのソース画面で、postCICSStockOrderService_request の定義にならって、新たにpostCICSStockOrderService_request_externalという構造体を追加し、リクエスト用のスキーマとして指定します。

```yaml:抜粋-URIパス部分
 /order:
    post:
      tags:
        - cicsstockrestfulservice
      operationId: postCICSStockOrderService
      parameters:
        - in: body
          name: postCICSStockOrderService_request
          description: request body
          required: true
          schema:
            $ref: '#/definitions/postCICSStockOrderService_request_external'
      responses:
        '200':
          description: OK
          schema:
            $ref: '#/definitions/postCICSStockOrderService_response_200_external'
        '400':
          description: Bad Request
          schema:
            $ref: '#/definitions/postCICSStockOrderService_response_400'
```

```yaml:抜粋-Requestデータ構造部分
  postCICSStockOrderService_response_200_external:
    type: object
    properties:
      stockOrder:
        type: object
        properties:
          returnCodeEx:
            type: integer
            minimum: 0
            maximum: 99
          responseMessageEx:
            type: string
            maxLength: 80
          stockOfItemList:
            type: array
            items:
              type: object
              properties:
                itemId:
                  type: string
                  maxLength: 8
                stockYN:
                  type: string
                  maxLength: 1
                stockNum:
                  type: integer
                  minimum: 0
                  maximum: 9999999
            maxItems: 6
            minItems: 6
```

ここでは、returnCodeの代わりにreturnCodeEx, responseMessageの代わりにresponseMessageExというフィールドを定義しています(名前を変えているだけ)。実質的な意味はあまりありません。

### 出力データ構造のマッピング

新たに定義した出力データ構造(公開用I/F②)と、バックエンドアクセス用の出力データ構造(公開用I/F①)のマッピングを行います。アセンブル画面でmapというポリシーで実装します。以下のようなイメージでマッピング情報を定義していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/7823e397-c62a-21d9-cf7e-a56186b102de.png)

order用のInvokeの後ろに、mapを挿入します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/a21ccf61-3f24-7845-9056-060448c13b87.png)

入力の右の編集ボタンをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/d88d2550-68dd-e173-06bc-5395111051b1.png)

「+入力」ボタンを押して以下のように指定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/27cb4ef1-afc4-051f-be0f-b4f581fbefd3.png)
コンテキスト変数: message.body
名前: 任意の名前
コンテンツ・タイプ: application/json
定義: #/definitions/postCICSStokOrderService_response_200 (元の出力用データ構造)

前の画面で出力の右の編集ボタンをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/8365f095-19c8-75b9-a7c5-ef733849d08e.png)

「+出力」ボタンを押して以下のように指定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/44b36a11-ffed-e357-e8aa-70937b9a3823.png)
コンテキスト変数: message.body
名前: 任意の名前
コンテンツ・タイプ: application/json
定義: #/definitions/postCICSStokOrderService_response_200_external (上で新規に定義した構造体)

入力、出力それぞれで指定したデータ定義が表示されるので、対応するフィールドを線でつないでマッピングを行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/23502938-1df1-5934-9c67-00330c9c4fad.png)

ここでは、名前を変更しているだけなので、それぞれ対応するフィールドを線で結びます。階層になっているフィールドは親の項目名を結べばOkのようです。

## API定義まとめ

上で設定したアセンブルの定義はyamlのAPI定義に落とし込まれます。
最終的に生成されるyamlは以下のようになります。

<details><summary>API定義</summary><div>

```yaml
swagger: '2.0'
info:
  description: ''
  version: 1.0.0
  title: test01
  x-ibm-name: test01
host: 'localhost:8080'
basePath: /test01/v1/isecics
schemes:
  - https
consumes:
  - application/json
produces:
  - application/json
paths:
  /order:
    post:
      tags:
        - test01
      operationId: postCICSStockOrderService
      parameters:
        - in: body
          name: postCICSStockOrderService_request
          description: request body
          required: true
          schema:
            $ref: '#/definitions/postCICSStockOrderService_request_external'
      responses:
        '200':
          description: OK
          schema:
            $ref: '#/definitions/postCICSStockOrderService_response_200_external'
        '400':
          description: Bad Request
          schema:
            $ref: '#/definitions/postCICSStockOrderService_response_400'
  '/stock/{groupCode}':
    get:
      tags:
        - test01
      operationId: getCICSStockCheckService
      parameters:
        - name: groupCode
          in: path
          required: true
          type: string
          maxLength: 4
      responses:
        '200':
          description: OK
          schema:
            $ref: '#/definitions/getCICSStockCheckService_response_200_external'
        '400':
          description: Bad Request
          schema:
            $ref: '#/definitions/getCICSStockCheckService_response_400'
definitions:
  getCICSStockCheckService_response_400:
    type: object
    properties:
      stockCheck:
        type: object
        properties:
          returnCode:
            type: integer
            minimum: 0
            maximum: 99
          responseMessage:
            type: string
            maxLength: 80
  getCICSStockCheckService_response_200:
    type: object
    properties:
      stockCheck:
        type: object
        properties:
          returnCode:
            type: integer
            minimum: 0
            maximum: 99
          responseMessage:
            type: string
            maxLength: 80
          itemList:
            type: array
            items:
              type: object
              properties:
                itemId:
                  type: string
                  maxLength: 8
                itemName:
                  type: string
                  maxLength: 66
                itemPrice:
                  type: integer
                  minimum: 0
                  maximum: 9999999
                stored:
                  type: string
                  maxLength: 1
                shipFrom:
                  type: string
                  maxLength: 64
            maxItems: 6
            minItems: 6
  getCICSStockCheckService_response_200_external:
    type: object
    properties:
      stockCheck:
        type: object
        properties:
          returnCodeEx:
            type: integer
            minimum: 0
            maximum: 99
          responseMessageEx:
            type: string
            maxLength: 80
          itemListEx:
            type: array
            items:
              type: object
              properties:
                itemId:
                  type: string
                  maxLength: 8
                itemName:
                  type: string
                  maxLength: 66
                itemPrice:
                  type: integer
                  minimum: 0
                  maximum: 9999999
                stored:
                  type: string
                  maxLength: 1
                shipFrom:
                  type: string
                  maxLength: 64
            maxItems: 6
            minItems: 6
  postCICSStockOrderService_request:
    type: object
    properties:
      stockOrder:
        type: object
        properties:
          orderList:
            type: array
            items:
              type: object
              properties:
                itemId:
                  type: string
                  maxLength: 8
                itemQuantity:
                  type: integer
                  minimum: 0
                  maximum: 9999999
            maxItems: 6
            minItems: 6
  postCICSStockOrderService_request_external:
    type: object
    properties:
      stockOrder:
        type: object
        properties:
          dummyData01Ex:
            type: string
            maxLength: 8
          testAuth:
            type: string
            maxLength: 30
          orderListEx:
            type: array
            items:
              type: object
              properties:
                itemId:
                  type: string
                  maxLength: 8
                itemQuantity:
                  type: integer
                  minimum: 0
                  maximum: 9999999
            maxItems: 6
            minItems: 6
  postCICSStockOrderService_response_400:
    type: object
    properties:
      stockOrder:
        type: object
        properties:
          returnCode:
            type: integer
            minimum: 0
            maximum: 99
          responseMessage:
            type: string
            maxLength: 80
  postCICSStockOrderService_response_200:
    type: object
    properties:
      stockOrder:
        type: object
        properties:
          returnCode:
            type: integer
            minimum: 0
            maximum: 99
          responseMessage:
            type: string
            maxLength: 80
          stockOfItemList:
            type: array
            items:
              type: object
              properties:
                itemId:
                  type: string
                  maxLength: 8
                stockYN:
                  type: string
                  maxLength: 1
                stockNum:
                  type: integer
                  minimum: 0
                  maximum: 9999999
            maxItems: 6
            minItems: 6
  postCICSStockOrderService_response_200_external:
    type: object
    properties:
      stockOrder:
        type: object
        properties:
          returnCodeEx:
            type: integer
            minimum: 0
            maximum: 99
          responseMessageEx:
            type: string
            maxLength: 80
          stockOfItemList:
            type: array
            items:
              type: object
              properties:
                itemId:
                  type: string
                  maxLength: 8
                stockYN:
                  type: string
                  maxLength: 1
                stockNum:
                  type: integer
                  minimum: 0
                  maximum: 9999999
            maxItems: 6
            minItems: 6
x-ibm-configuration:
  enforced: true
  testable: true
  phase: realized
  cors:
    enabled: true
  assembly:
    execute:
      - operation-switch:
          title: operation-switch
          case:
            - operations:
                - getCICSStockCheckService
              execute:
                - invoke:
                    title: invoke-stockCheck
                    timeout: 60
                    verb: GET
                    cache-response: protocol
                    cache-ttl: 900
                    stop-on-error:
                      - null
                    version: 1.0.0
                    target-url: 'https://xxxxx.securegateway.appdomain.cloud:15236/showroomdemo/v1/isecics/stock/$(request.parameters.groupCode)'
                    secure-gateway: false
                    tls-profile: zosconnecttls01
                    username: ZCON01
                    password: xxxxxxxx
                - map:
                    title: map stock response
                    inputs:
                      message_body:
                        schema:
                          $ref: '#/definitions/getCICSStockCheckService_response_200'
                        variable: message.body
                        content: application/json
                    outputs:
                      message_body_ex:
                        schema:
                          $ref: '#/definitions/getCICSStockCheckService_response_200_external'
                        variable: message.body
                        content: application/json
                    actions:
                      - set: message_body_ex.stockCheck.returnCodeEx
                        from: message_body.stockCheck.returnCode
                      - set: message_body_ex.stockCheck.responseMessageEx
                        from: message_body.stockCheck.responseMessage
                      - set: message_body_ex.stockCheck.itemListEx
                        from: message_body.stockCheck.itemList
                    version: 1.0.0
                    options: {}
            - operations:
                - postCICSStockOrderService
              execute:
                - map:
                    title: map order request
                    inputs:
                      request_body_ex:
                        schema:
                          $ref: '#/definitions/postCICSStockOrderService_request_external'
                        variable: request.body
                        content: application/json
                    outputs:
                      message_body:
                        schema:
                          $ref: '#/definitions/postCICSStockOrderService_request'
                        variable: message.body
                        content: application/json
                      header_authorization:
                        schema:
                          type: string
                        variable: message.headers.Authorization
                    actions:
                      - set: message_body.stockOrder.orderList
                        from: request_body_ex.stockOrder.orderListEx
                      - set: header_authorization
                        from: request_body_ex.stockOrder.testAuth
                    version: 1.0.0
                    options: {}
                - invoke:
                    title: invoke-stockOrder
                    timeout: 60
                    verb: POST
                    cache-response: protocol
                    cache-ttl: 900
                    stop-on-error:
                      - null
                    version: 1.0.0
                    target-url: 'https://xxxxx.securegateway.appdomain.cloud:15236/showroomdemo/v1/isecics/order'
                    tls-profile: zosconnecttls01
                - map:
                    title: map order response
                    inputs:
                      message_body:
                        schema:
                          $ref: '#/definitions/postCICSStockOrderService_response_200'
                        variable: message.body
                        content: application/json
                    outputs:
                      message_body_external:
                        schema:
                          $ref: '#/definitions/postCICSStockOrderService_response_200_external'
                        variable: message.body
                        content: application/json
                    actions:
                      - set: message_body_external.stockOrder.returnCodeEx
                        from: message_body.stockOrder.returnCode
                      - set: message_body_external.stockOrder.responseMessageEx
                        from: message_body.stockOrder.responseMessage
                      - set: message_body_external.stockOrder.stockOfItemList
                        from: message_body.stockOrder.stockOfItemList
                    version: 1.0.0
                    options: {}
          otherwise: []
          version: 1.0.0
    catch: []
securityDefinitions:
  Client Secret:
    type: apiKey
    description: ''
    in: header
    name: X-IBM-Client-Secret
  Client ID:
    type: apiKey
    description: ''
    in: header
    name: X-IBM-Client-Id
security:
  - Client Secret: []
    Client ID: []

```

</div></details>


## テスト実行

アセンブル画面でAPI呼び出しのテストをしてみます。
request.bodyには、公開用I/F②として定義したスキーマに従って組み立てたJSONデータを指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/4fd52d51-e8bc-92cd-3f21-00fb55b3a02a.png)

返される結果も公開用I/F②として定義したスキーマに従ったJSONデータになっていることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/14509222-e0b4-5d3d-a899-81bc74376acf.png)


# おわりに
COBOL, z/OS Connect, API Connect それぞれでインターフェースの浄化についての操作がどのように行えるか見てきました。
COBOL => z/OS Connect => API Connectとカスタマイズできる自由度は増していくイメージです。上の例ではシンプルなケースでの手順を示しましたが、特にAPI Connectのアセンブルでは様々なロジックを柔軟に組み込むことができるようなので、もっと様々なカスタマイズを行える余地があります。ただし複雑なロジックを組み込めばそれだけAPIリクエスト時のパフォーマンスに影響することになるので、できるだけ必要最小限のカスタマイズに抑え、充分なパフォーマンステストも実施する必要があると思われます。