【CAP】CAPで作ったODataをUI5アプリで使う(4) V2版ElementsアプリをFiori Toolsで

##はじめに
この記事は、「[CAPで作ったODataをUI5アプリで使う](https://qiita.com/tami/items/d1b69a93da14e288faf2)」シリーズの4回目です。
[前回](https://qiita.com/tami/items/bade4fd014a7e57014a4)の記事ではCAPのOData V4のサービスElementsアプリから利用しました。今回は、V2のサービスをElementsアプリから利用する方法について調べます。

##V2版ElementsアプリをMTA + Approuter方式で作るには？
Cloud Foundry用のアプリとして作成するので、前回までと同様にMTA + Approuter方式を採用したいと思いました。

私は通常、ABAPのODataサービスを使ってElementsアプリを作るときはWebIDEのテンプレートを使用しています。CAPのサービスも、Neo環境にDestinationを作ってやればWebIDEから利用することができるようです。
参考：[Developing Fiori Element Application (List Report and Object Page) consuming the OData service created using SAP Cloud Application Programming model](https://blogs.sap.com/2020/06/08/developing-fiori-element-application-list-report-and-object-page-consuming-the-odata-service-created-using-sap-cloud-application-programming-model/)

ですが、CAPサービスがCloud FoundryにあるのにわざわざNeoにDestinationを作るのもな～～と思い、別の方法でできないか検討してみました。

###Fiori Toolsは使える？
そこで浮かんだのが[Fiori Tools](https://blogs.sap.com/2020/06/22/sap-fiori-tools-is-generally-available.-increase-the-efficiency-of-developing-sap-fiori-elements-apps/)です。Fiori Toolsは2020年6月に公開されたツールで、SAP Business Application StudioやVS CodeでFiori Elementsのテンプレートを選んでアプリを作成できます。ただ現在のところ、ABAPシステム（クラウド or オンプレ）へのデプロイにしか対応していません。そこで、**Fiori Toolsで生成したアプリを色々いじってCloud Foundryにデプロイする**という作戦で行くことにしました。
あまりきれいなやり方ではないし、これがベストではないと思いますが、「やってみた」シリーズということでご容赦ください。

##今回やること
1. Elementsプロジェクトを作成
2. Fiori ToolsでUIモジュールを生成
3. Cloud Foundryで動かすための調整
4. Cloud Foundryにデプロイするための調整
5. デプロイ
6. ローカルアノテーションを追加

###前提
開発環境はVS Codeです。
以下からFiori Toolsをインストールしておきます。
https://marketplace.visualstudio.com/items?itemName=SAPSE.sap-ux-fiori-tools-extension-pack

##1. Elementsプロジェクトを作成
おなじみのyomanのgeneratorを使ってプロジェクトを生成します。

```
yo easy-ui5
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/538373d4-6ee9-ee66-6bf0-6076ab8aacbb.png)

uimoduleをFiori Toolsで生成するプロジェクトに置き換えたいので、uimoduleフォルダはバッサリと削除してしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/d57bb87b-fb23-a273-6278-a75b4202278e.png)

##2. Fiori ToolsでUIモジュールを生成
Ctrl + Shift + Pでコマンドパレットを出して、SAP Fiori tools - Application Generator: Launchを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/e1ca3628-e9fe-1131-dcea-12b2c9d37dda.png)

SAP Fiori tools - Application Generatorを選択して次に進みます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/92c43bb5-12c9-9a6f-df63-270193a5edbd.png)
List Report Object Page V2を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/a3f82480-4bd7-68e8-8a7b-d830c2b1f0d5.png)
Data Sourceに"Connect to an OData Service"を選択します。URLには、[CAPで作ったODataサービス](https://qiita.com/tami/items/d1b69a93da14e288faf2)のURLを入力します。Main EntityにはProductsを指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/725070ad-f994-0bbd-3849-d142c6be5fa0.png)
モジュール名、タイトル、名前空間、説明を入力し、最後にプロジェクトフォルダを選択します。ここではステップ1で作成したフォルダを指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/c7f8aa75-4379-9214-20bf-7c63cd8e1085.png)
結果、以下のようにプロジェクトフォルダが生成されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/2512b82b-507a-ea7d-f623-203e3f4d0708.png)
フォルダ名をuimoduleに変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/98d8efa1-04cf-a4e4-c03f-4a68f3c04c7c.png)

###実行してみる
uimoduleに移動して、`npm start`と入力します。
すると、ブラウザが立ち上がりFiori Elementsアプリが表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/d1798e15-c2f8-929d-3e4f-e5a0daf58ef8.png)

##3. Cloud Foundryで動かすための調整
ローカルで実行した場合、uimoduleの中の設定だけで動いている状態なので、Cloud Foundryに持って行ったときにちゃんと動くように調整します。

###3.1. Approuterの設定
まず、webappフォルダの中にxs-app.jsonファイルを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/3b6fae63-3740-7754-b68f-a585daa28969.png)

以下を入力します。UIモジュールはHTML5リポジトリにデプロイされるので、実行時にそこを見に行くための設定です。welcomeFileとして指定したindex.htmlはこのあと作ります。

```json:uimodule/webapp/xs-app.jton
{
  "welcomeFile": "/index.html",
  "routes": [
    {
      "source": "^(.*)",
      "target": "$1",
      "authenticationType": "xsuaa",
      "service": "html5-apps-repo-rt"
    }
  ]
}
```

続いて、このアプリの入り口になるほうのApprouterの設定です。ここでは、ODataサービスへのルーティングの設定を行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/2ece2159-4ae7-08d2-852a-e6c3844757e5.png)
その前に、manifest.jsonを見てみましょう。ODataのuriが/v2/product/となっています。

```json:uimodule/webapp/manifest.json
        "dataSources": {
            "mainService": {
                "uri": "/v2/product/",
                "type": "OData",
                "settings": {
                    "annotations": [
                        "annotation"
                    ],
                    "localUri": "localService/metadata.xml"
                }
            },
```
以下はui5.yamlの設定です。ローカルで実行した場合、/v2で始まるurlがfiori-tools-proxyというミドルウェアによって本来の宛先に中継されるようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/401c1f77-147d-adab-8e78-c3c318c2c96b.png)
Approuterでも同様に、/v2/で始まるurlを中継するように設定します。ルーティングされる際にv2をつけたままにしたいので、targetを"/v2/$1"としています。

```json:approuter/xs-app.json
{
  "welcomeFile": "demoproductsv2elements/",
  "authenticationMethod": "none",
  "logout": {
    "logoutEndpoint": "/do/logout"
  },
  "routes": [
    {
      "source": "^/v2/(.*)$",
      "target": "/v2/$1",
      "authenticationType": "xsuaa",
      "destination": "cap-products",
      "csrfProtection": false
    }
  ]
}
```
###3.2. index.htmlファイルの作成
webapp直下にindex.htmlファイルを作成します。これがアプリ実行時に呼ばれる画面になります。内容は[V4版Elementsアプリ](https://qiita.com/tami/items/bade4fd014a7e57014a4#31-fiori-launchpad%E3%82%B5%E3%83%B3%E3%83%89%E3%83%9C%E3%83%83%E3%82%AF%E3%82%B9%E7%94%BB%E9%9D%A2%E3%82%92%E4%BD%9C%E6%88%90)と同じです。

```html:webapp/index.html
<!DOCTYPE html>
<html>
<head>

    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Bookshop</title>

    <script>
        window["sap-ushell-config"] = {
            defaultRenderer: "fiori2",
            applications: {
                "browse-products": {
                    title: "Browse Products",
                    description: "list",
                    additionalInformation: "SAPUI5.Component=demo.productsv2elements",
                    applicationType : "URL",
                    url: ".",
                    navigationMode: "embedded"
                }
            }
        };
    </script>

    <script src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
    <script src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
        data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout"
        data-sap-ui-compatVersion="edge"
        data-sap-ui-theme="sap_fiori_3"
        data-sap-ui-frameOptions="allow"
    ></script>
    <script>
        sap.ui.getCore().attachInit(()=> sap.ushell.Container.createRenderer().placeAt("content"))
    </script>

</head>
<body class="sapUiBody" id="content"></body>
</html>
```

##4. Cloud Foundryにデプロイするための調整
そのままビルド、デプロイしようとしたところ色々なエラーが起きました。問題は結局、ui5.yamlファイルの設定に起因していました。修正後のui5.yamlは以下のようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/8e93d5e5-72ec-cba4-0dd5-db8b0243f786.png)

```yaml:uimodule/ui5.yaml
specVersion: '2.0'
metadata:
  name: 'productsv2elements'
type: application
resources:
  configuration:
    paths:
      webapp: uimodule/webapp
server:
  customMiddleware:
    - name: fiori-tools-proxy
      afterMiddleware: compression
      configuration:
        ignoreCertError: false # If set to true, certificate errors will be ignored. E.g. self-signed certificates will be accepted
        backend:
          - path: /v2
            url: https://65d11c82trial-mio-trial-dev-products-srv.cfapps.eu10.hana.ondemand.com
        ui5:
          path:
            - /resources
            - /test-resources
          url: https://ui5.sap.com
          version:  # The UI5 version, for instance, 1.78.1. Empty means latest version
    - name: fiori-tools-appreload
      afterMiddleware: compression
      configuration:
        port: 35729
        path: webapp
builder:
  customTasks:
    - name: "ui5-task-zipper"
      afterTask: "uglify"
      configuration:
        archiveName: "uimodule"
        additionalFiles:
        - approuter/xs-app.json      

```

##5. デプロイ
プロジェクトのルートに移動し、以下のコマンドでデプロイします。

```
npm run deploy
```
全てうまくいけば、アプリケーションのURLが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/907585cf-afc1-664d-a23e-7ccb2bf8f89d.png)
実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/8a72d47c-8ddf-168c-f5c6-604a83293e46.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/8faa880e-6571-4026-a92b-c22029d47bb4.png)

##6. ローカルアノテーションを追加
せっかくFiori Toolsを使っているので、ツールを使ってローカルアノテーションをでテーブル列を追加してみましょう。

Ctrl + Shift + Pでコマンドパレットを出して、SAP Fiori tools - Service Modeloer選択します。（対象のプロジェクトが複数でてきたら、今のプロジェクトのuimoduleフォルダを選択します）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/a82626a9-f00c-ac26-1b39-1e9150cb8fc3.png)
ProductService.Productsの横にある@マークをクリックしてアノテーションを表示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/2a3d4baa-6a5e-920c-3527-0ee3088e9783.png)
Ui.LineItemの横にあるコピーボタンを押すと、アノテーションがローカルにコピーされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/d58ef988-00c5-ea05-5691-41133800f332.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/04021763-fd2a-bb57-8043-a51251fa2ba8.png)

productTypeの列を追加します。入力中にCtrl + Shiftを押すと候補を表示してくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/47b4b74e-7445-8195-8568-6c642b6596a1.png)

```xml
                <Annotation Term="UI.LineItem">
                    <Collection>
                        <Record Type="UI.DataField">
                            <PropertyValue Property="Value" Path="product"/>
                        </Record>
                        <Record Type="UI.DataField">
                            <PropertyValue Property="Value" Path="description"/>
                        </Record>
                        <Record Type="UI.DataField">
                            <PropertyValue Property="Value" Path="productType"/>
                        </Record>
                    </Collection>
                </Annotation>
```

以下はローカル実行の結果ですが、Product Typeが追加されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/cc0e80b2-2946-f6ff-61df-0dd5735e7e68.png)

##おわりに
これまで、[CAPで作ったODataサービス](https://qiita.com/tami/items/d1b69a93da14e288faf2)を[フリースタイルのUI5アプリ](https://qiita.com/tami/items/55153c92fffc159e40d8)、[V4版Elementsアプリ](https://qiita.com/tami/items/bade4fd014a7e57014a4)、そしてV2版Elementsアプリから利用する方法について見てきました。UI編の最後として、次回はCAPプロジェクトの中にUIモジュールがあるMTAのパターンを実装してみたいと思います。
UIはやはりODataとの結びつきが強いので、CAPプロジェクトの中で作ったほうがいいかも、と思い始めています。CAPプロジェクトの中にFiori ToolsでUIモジュールを作るのが最強な気がしています。（アノテーションを活用するという意味で）