【和訳】Composer クエリ言語と REST API を用いたチュートリアル

# はじめに

和訳第五弾。

[Queries Tutorial using the Composer Query language and REST APIs](https://hyperledger.github.io/composer/latest/tutorials/queries) の和訳です。
ガバガバ和訳でも英語読むよりマシという方は御覧ください。
関連チュートリアルに関しても（たぶん）上げます。
当方 Hyperledger Composer について学び始めたばかりであるため、指摘等大歓迎です。

---

本チュートリアルでは、[開発者向けチュートリアル](https://qiita.com/Saya_K/items/8fae107fce77afba95ee) を拡張し、クエリの説明を行います。ネイティブのクエリ言語は、条件を指定して返された結果をフィルタリングし、リザルトセット内の資産の更新や削除のような操作を実行するためにトランザクション内で呼び出されます。

クエリは、ビジネスネットワークが定義されているディレクトリの親ディレクトリにあるクエリファイル（**.qry**）で定義されます。クエリは資産または参加者を選択する条件を定義する WHERE 句を含みます。

本チュートリアルでは、[開発者向けチュートリアル](https://qiita.com/Saya_K/items/8fae107fce77afba95ee) で開発、デプロイした **tutorial-network** ビジネスネットワークを使用します。

# 要求事項

チュートリアル実行前に以下の項目を行ってください。

- [開発環境の構築](https://qiita.com/Saya_K/items/82dae077eca669840aa5)
- [開発者向けチュートリアル](https://qiita.com/Saya_K/items/8fae107fce77afba95ee)

# Step 1. ビジネスネットワークの更新

開発者向けチュートリアルで作成したビジネスネットワークの更新が必要です。更新後のネットワークには2つのイベントと追加のトランザクションが含まれます。

## modelファイルの更新

イベントと新規トランザクションを model ファイルに追加します。

1. **tutorial-network** の model（**.cto**）ファイルを開きます。
2. 下記のイベントとトランザクションを追加します。

    ```&nbsp;.cto
    event TradeNotification {
        --> Commodity commodity
    }
     
    transaction RemoveHighQuantityCommodities {
    }
     
    event RemoveNotification {
        --> Commodity commodity
    }
    ```

3. 更新を保存します。

## クエリとイベントを扱うためのトランザクションの更新

ドメインモデルの更新を行ったので、トランザクションが送信された時に実行されるビジネスロジックを追加します。

1. トランザクション処理機能ファイル **lib/logic.js** を開きます。
2. 下記の内容に書き換えます。

    ```lib/logic.js
    /**
     * Track the trade of a commodity from one trader to another
     * @param {org.example.mynetwork.Trade} trade - the trade to be processed
     * @transaction
     */
    async function tradeCommodity(trade) {
        // set the new owner of the commodity
        trade.commodity.owner = trade.newOwner;
        let assetRegistry = await getAssetRegistry('org.example.mynetwork.Commodity');
     
        // emit a notification that a trade has occurred
        let tradeNotification = getFactory().newEvent('org.example.mynetwork', 'TradeNotification');
        tradeNotification.commodity = trade.commodity;
        emit(tradeNotification);
     
        // persist the state of the commodity
        await assetRegistry.update(trade.commodity);
    }
     
    /**
     * Remove all high volume commodities
     * @param {org.example.mynetwork.RemoveHighQuantityCommodities} remove - the remove to be processed
     * @transaction
     */
    async function removeHighQuantityCommodities(remove) {
        let assetRegistry = await getAssetRegistry('org.example.mynetwork.Commodity');
        let results = await query('selectCommoditiesWithHighQuantity');
     
        for (let n = 0; n < results.length; n++) {
            let trade = results[n];
     
            // emit a notification that a trade was removed
            let removeNotification = getFactory().newEvent('org.example.mynetwork','RemoveNotification');
            removeNotification.commodity = trade;
            emit(removeNotification);
            await assetRegistry.remove(trade);
        }
    }
    ```

3. 変更を保存します。

1つ目の関数 **tradeCommodity** は、Trade トランザクションで商品のオーナーを変更し、その結果の通知イベントを発行します。その後、変更された商品を、商品インスタンス格納に利用されている資産レジストリに永続化します。

2つ目の関数は、名前付きクエリ **selectCommoditiesWithHighQuantity** （**queries.qry** で定義）を呼び出します。これは、quantity > 60 のすべての商品レコードを返し、イベントを発行し、資産レジストリからそれらの商品を削除します。

# Step 2.クエリ定義ファイルの作成

トランザクション処理ロジックで使用されるクエリは **queries.qry** ファイルに定義されています。各クエリエントリは、クエリが実行されるリソースと条件を定義します。

1. **tutorial-network** ディレクトリに新規ファイル **queries.qry** を作成します。
2. 下記の内容を貼り付けます。

    ```queries.qry
    /** Sample queries for Commodity Trading business network
    */
     
    query selectCommodities {
      description: "Select all commodities"
      statement:
          SELECT org.example.mynetwork.Commodity
    }
     
    query selectCommoditiesByExchange {
      description: "Select all commodities based on their main exchange"
      statement:
          SELECT org.example.mynetwork.Commodity
              WHERE (mainExchange==_$exchange)
    }
 
    query selectCommoditiesByOwner {
      description: "Select all commodities based on their owner"
      statement:
          SELECT org.example.mynetwork.Commodity
              WHERE (owner == _$owner)
    }
     
    query selectCommoditiesWithHighQuantity {
      description: "Select commodities based on quantity"
      statement:
          SELECT org.example.mynetwork.Commodity
              WHERE (quantity > 60)
    }
    ```

3. 変更を保存します。

# Step 3. ビジネスネットワークアーカイブの再生成

ビジネスネットワークのファイルを変更した場合は、ビジネスネットワークアーカイブ（**.bna**）としての再パケージングと、Hyperledger Fabric インスタンスへの再デプロイが必要です。デプロイされたネットワークを更新するには、新しいバージョン番号を振る必要があります。

1. **tutorial-network** ディレクトリで **package.json** ファイルを開きます。
2. **version** プロパティを **0.0.1** から **0.0.2** に更新します。
3. コマンドラインを使用し、 **tutorial-network** ディレクトリへ移動します。
4. 下記のコマンドを実行します。

    ```
    composer archive create --sourceType dir --sourceName . -a tutorial-network@0.0.2.bna
    ```

# Step 4. 更新版ビジネスネットワーク定義をデプロイする

ブロックチェーンを最新版にするためには、修正したネットワークをデプロイする必要があります。既存のデプロイされたビジネスネットワークを更新するために、新規生成されたビジネスネットワークアーカイブファイルを使用します。これは、開発者向けチュートリアルで使用したものと同一のビジネスネットワーク名です。

1. ターミナルで **tutorial-network@0.0.2.bna** を含むディレクトリへ移動します。
2. 下記のコマンドを実行し、更新後のビジネスネットワークをインストールします。

    ```
    composer network install --card PeerAdmin@hlfv1 --archiveFile tutorial-network@0.0.2.bna
    ```

3. 下記のコマンドを実行し、新規バージョンに更新します。

    ```
    composer network upgrade -c PeerAdmin@hlfv1 -n tutorial-network -V 0.0.2
    ```

4. 下記のコマンドを実行し、現在のバージョンを確認します。

    ```
    composer network ping -c admin@tutorial-network | grep Business
    ```

# Step 5. 更新版ビジネスネットワーク用REST APIの再生成

最新版のビジネスネットワークとクエリを統合し、ビジネスネットワーク用の REST API を公開します。

1. コマンドラインを使用し、**tutorial-network** ディレクトリへ移動します。
2. 下記のコマンドを用いて、 REST サーバを起動します。

    ```
    composer-rest-server
    ```

3. カードネームとして **admin@tutorial-network** を入力します。
4. API でネームスペースを使用するかどうかは、**never use namespace** を選択します。
5. API を保護するかどうかは、**No** を選択します。
6. パスポートを使用して認証を行うかどうかは、**No** を選択します。
7. explorer テストインターフェイスを使用するかどうかは、**Yes** を選択します。
8. ダイナミックロギングは設定しません。
9. イベントの発行を有効にするかどうかは、**Yes** を選択します。
10. TLS を有効にするかどうかは、**No** を選択します。

# Step 6. REST APIのテストとデータの作成

ブラウザで http://localhost:3000/explorer を開きます。LoopBack API Explorer が表示され、生成された REST API を調べてテストを行うことができるようになりました。

Query と呼ばれるエンドポイントが追加され、クリックして展開すると **tutorial-network** で定義されている REST クエリ操作のリストが表示されていることがわかります。

次へ進む前に、適切にクエリを試行するためにデータを作成する必要があります。サンプル JSON データを使用して、REST API で3人のトレーダー（参加者）といくつかの商品（資産）を作成します。

1. REST エクスプローラで **Trader** をクリックし、**POST** /Trader をクリックし、Parameters に下記 Trader インスタンスの内容を記述し実行します。

    ```JS:Trader&nbsp;Instance
    {
      "$class": "org.example.mynetwork.Trader",
      "tradeId": "TRADER1",
      "firstName": "Jenny",
      "lastName": "Jones"
    }
    ```

2. **Try it out!** をクリックし、参加者を作成します。 **Responce Code** が 200（SUCCESS）で返ります。
3. 下記内容で別の参加者を作成します。

    ```JS:Trader&nbsp;Instance
    {
      "$class": "org.example.mynetwork.Trader",
      "tradeId": "TRADER2",
      "firstName": "Jack",
      "lastName": "Sock"
    }
    ```

4. 下記内容で別の参加者を作成します。

    ```JS:Trader&nbsp;Instance
    {
      "$class": "org.example.mynetwork.Trader",
      "tradeId": "TRADER3",
      "firstName": "Rainer",
      "lastName": "Valens"
    }
    ```

5. **Commodity** をクリックします。
6. Trader 同様、Parameters に書きの内容を記述し2つの資産をそれぞれ TRADER1、TRADER2 用に作成します。

    ```JS:Commodity&nbsp;Instance
    {
      "$class": "org.example.mynetwork.Commodity",
      "tradingSymbol": "EMA",
      "description": "Corn",
      "mainExchange": "EURONEXT",
      "quantity": 10,
      "owner": "resource:org.example.mynetwork.Trader#TRADER1"
    }
    ```

    ```JS:Commodity&nbsp;Instance
    {
      "$class": "org.example.mynetwork.Commodity",
      "tradingSymbol": "CC",
      "description": "Cocoa",
      "mainExchange": "ICE",
      "quantity": 80,
      "owner": "resource:org.example.mynetwork.Trader#TRADER2"
    }
    ```

# Step 7. 商品取引 REST API エクスプローラを用いたクエリの実行

資産と参加者を作成したので、生成された Query REST 操作を使用してクエリをテストすることができます。

## シンプルな REST クエリの実行

資産と参加者を作成したのでクエリを試すことができます。

最初に試す最もシンプルな REST クエリは、名前付きクエリ **selectCommodities** です。

**Query** REST エンドポイントをクリックで展開すると、モデルに定義した名前付きクエリが表示されます。

これらのクエリは REST クエリとして公開され、クエリにアクセスするために /GET 操作が生成されています。クエリの説明（モデル定義で定義したもの）が右側に表示されていることに注意してください。

1. **selectCommodities** クエリを展開してください。
2. **Try it out!** ボタンをクリックします。すべての既存の商品（現時点で2件存在）が返ります。

## フィルタリングされた REST クエリの実行

Exchange（例：EURONEXT）で商品をフィルタリングして取得します。

1. **selectCommoditiesByExchange** を展開し、**Parameters** へスクロールします。
2. **exchange** パラメータに **EURONEXT** を入力します。
3. **Try it out!** をクリックします。レスポンスボディとして Exchange が **EURONEXT** の商品のみが表示されます。

## 名前付きクエリの結果を利用したトランザクション更新の実行

最後に、クエリファイルで quantity > 60 の商品をフィルタリングするシンプルなクエリを定義したことを思い出してください。クエリはトランザクション処理で利用すると非常に強力です。クエリを使用すると、例えば、トランザクションロジックで更新や削除アクションの作成をしたりするための、一連の資産や参加者を設定する事ができるようになります。

**removeHighQuantityCommodities** トランザクション内の **selectCommoditiesWithHighQuantity** クエリを使用します。REST エクスプローラでこのクエリの /GET 操作を実行すると、quantity > 60 の資産のみが取得されます。

High Quantity 商品の削除を実行するためにクエリを使用しましょう。

まず、商品がどれだけ存在するか確認を行うと（**Commodity** /GET 操作を使用）、最低でも2つの商品が存在し、一つは（Cocoa）quantity > 60 となっています。

**/selectCommoditiesWithHighQuantity** REST エンドポイントをクリックし、/GET をクリックし、**Try it out!** をクリックして、実際のクエリを確認しましょう。基準に一致した1件の商品が表示されます。

削除対象商品の決定に **High Quantity** クエリ定義を利用する REST トランザクションを実行しましょう。/POST 操作を表示するために**RemoveHighQuantityCommodities** REST エンドポイントをクリックします。

POST をクリックし、**Try it out!** をクリックします。

**NOTE：****data** セクションには何も入力しません。

下にスクロールすると、トランザクション処理関数内の **remove** 呼び出し（それ自体がブロックチェーントランザクション）を表し、状態を更新するトランザクション ID が表示されます。レスポンスコードは 200 です。

最後に、商品のステータスを確認しましょう。**Commodity** REST 操作へ戻り、/GET の **Try it out!** をクリックします。

結果から資産 **Cocoa** がなくなっている、すなわち quantity <= 60 の商品のみ、つまり資産 **Corn** が表示されていることがわかります。名前付きクエリはトランザクションの更新（high quantity 商品の削除のため）を提供し、それはビジネスロジックで実行されました。

# Congratulations!

これでチュートリアルは完了しました。Composer のクエリ機能について、これまで以上に優れたアイディアが得られたと思います。独自のクエリの作成・構築（または既存のクエリの改良とこのビジネスネットワークへの関連データの追加 - 注：クエリの変更をデプロイする必要があります）を試してみてください。