UI5でSAP CommunityブログのRSSリーダーを作る

## 背景
今年の1月にSAP Communityが新しいプラットフォームに移行され、困ったことが起きました。従来、ブログは直近で投稿されたものから順に表示されていたのですが、現在は"Recent Activity"と称して、過去のブログに最近コメントがついたものも上がってきてしまいます。コミュニティメンバーからも[不満の声](https://community.sap.com/t5/blogs/blogarticleprintpage/blog-id/whats-new/article-id/126/comment-id/216)が上がっていますが、まだ改善されません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/0deb1314-38b6-4637-5e84-1a13a4311813.png)

代替手段として、RSSフィードにより最新の投稿だけを取得する方法が提示されました。

https://community.sap.com/t5/what-s-new/exploring-the-rss-feeds-of-the-new-sap-community-platform/ba-p/13594677

「直近100件のブログを取得する」であれば、以下のURLから取得できます。
https://community.sap.com/khhcw49343/rss/Community?interaction.style=blog&feeds.replies=false&count=100

## RSSリーダー(Feeder) を使ってみる
まずは、[Feeder](https://feeder.co/reader)というRSSリーダーを使ってみました。基本的にはこれで十分だと思っています。ただ一つ問題があり、50件以上の投稿を読み込むには有料プランにアップグレードが必要です。毎日見に来るのであれば50件でも十分ですが、2～3日見られないことがあると50件では足りません。探せばほかに50件以上無料で見られるRSSリーダーがあるかもしれませんが、「読み込める投稿の数」という観点での比較が難しかったため自分で作ることにしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/52ac2fea-1d28-1e5b-5dc1-f8da4e158f3e.png)

## UI5で作ってみる
機能はシンプルに、「100件程度の新規投稿を一覧で表示し、クリックしたらブログのページを開く」ことです。帰省時などをメインの使用場面と考え、スマートフォンからも見られるようにMobile Startで実行できるようにします。

ソースコード：https://github.com/miyasuta/rss-reader

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/764db606-25e6-d629-8230-cb484001457d.png)

## 技術的なポイント
コード自体は簡単ですが、私にとって新しいチャレンジだったことが2つありました。

- XML Modelを使う
- 3rd-partyのライブラリを使う (ui5-tooling-modules)

### XML Modelを使う
RSS Feedは以下のようなXML形式で提供されるため、XMLモデルを作成してViewにバインドします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/8541a84b-767d-c4ca-fef4-1d2d53b94883.png)

XMLモデルの使い方は以下のドキュメントに書かれています。

https://sapui5.hana.ondemand.com/sdk/#/topic/a53e71d85fae4d0887a8b58431197a27.html

使ってみてわかったXMLModelの特徴は以下の二点です。
- XMLのルートの要素はパスに含めない
- ネームスペースがついたタグをバインドで使用する方法（注意点）

Viewのソースは以下のようになります。

```xml
<mvc:View controllerName="miyasuta.rssreader.controller.List"
    xmlns:mvc="sap.ui.core.mvc" displayBlock="true"
    xmlns="sap.m">
    <Page id="page" title="{i18n>title}">
        <headerContent>
			<Button id="_IDGenButton1" icon="sap-icon://synchronize" press="onSyncrhonize"/>
		</headerContent>
        <content>
            <List id="_IDGenList1"
                items="{/channel/item}"
                mode="SingleSelectMaster"
                selectionChange="onSelect">
                <FeedListItem id="_IDGenFeedListItem1"
                    icon="sap-icon://write-new-document"
                    info="{path:'dc:creator/text()'}"
                    timestamp="{path:'dc:date/text()', formatter: '.formatTimestamp'}"
                    text="{title/text()}"
                    >
                </FeedListItem>
            </List>
        </content>
    </Page>
</mvc:View>
```

Listにバインドするコレクションのパスは`/channel/item`となります。"rss"はルート要素なのでパスに含みません。
ネームスペースがついたタグをバインドする場合、`info="{dc:creator/text()}`の形式ではViewがエラーになります。このため`info="{path:'dc:creator/text()'}"`の形式でバインドする必要があります。

### 3rd-partyのライブラリを使う (ui5-tooling-modules)
RSSフィードでは投稿日（dc:date）がUTCのタイムゾーンになっています。これをローカルのタイムゾーンに変換するためフォーマッターを使用し、[moment](https://momentjs.com)というライブラリにより変換を行います。
UI5では[ui5-tooling-modules](https://github.com/ui5-community/ui5-ecosystem-showcase/tree/main/packages/ui5-tooling-modules#readme)という拡張により3rd-partyのライブラリを利用することができます。つまづいた部分もあったので、以下で使用方法について説明します。

参考：[SAP-samples/ui5-typescript-tutorial/Exercise 4 - Use Third-Party NPM Packages](https://github.com/SAP-samples/ui5-typescript-tutorial/tree/main/exercises/ex4)

#### 1. インストール
以下のコマンドによりui5-tooling-modulesをインストールします。

```
npm install ui5-tooling-modules --save-dev
```

#### 2. ui5.yamlの設定
ui5.yamlに以下の設定を追加します。server.customMiddlewareはローカル実行のための設定、builder.customTasksはビルドのための設定です。

```yaml
server:
  customMiddleware:
    - name: ui5-tooling-modules-middleware
      afterMiddleware: compression    
    - name: fiori-tools-proxy
      # afterMiddleware: compression
      afterMiddleware: ui5-tooling-modules-middleware
      ...
builder:
  customTasks:
    - name: ui5-tooling-modules-task
      afterTask: replaceVersion
      configuration: 
        addToNamespace: true
      ...
```
設定のポイント（注意点）は以下です。

- **customMiddlewareの設定**
fiori-tools-proxyを使用している場合、fiori-tools-proxyの`afterMiddleware`にui5-tooling-modules-middlewareを指定することによってfiori-tools-proxyがui5-tooling-modulesの後に動くようにします。この設定がないとfiori-tools-proxyが3rd-partyのモジュールを探しに行ってしまい、Not Foundとなります。

- **customTasksの設定**
ui5-tooling-modules-taskはcustomTasksセクションの先頭に追加します。ビルド時にここで指定したのと逆の順番でタスクが実行されるため、最後に実行したいui5-tooling-modules-taskを先頭に持ってきます。
また、`addToNamespace: true`を設定することで3rd-partyのモジュールが/dist/thrdpartyに格納され、Fiori Launchpad環境で利用できるようになります。

:::note warn
FioriのApplication Generatorで生成した場合、`build:mta`コマンドでビルドするとui5-yamlではなくui5-deploy.yamlの設定が使われます。このため、ui5-deploy.yamlにも上記のcustomTasksの設定を追加する必要があります。

:::

#### 3. 3rd-partyのモジュールのインストール
今回は以下のコマンドでmoment, moment-timezoneをインストールしました。~~これらはdev dependencyとして追加します。ui5-tooling-modulesがライブラリのバンドルをしてくれるので、デザインタイムにだけあればよいのです。~~
⇒dependenciesに追加しないと、ビルド結果にモジュールが含まれません。

```
npm install moment moment-timezone
```

#### 4. コントローラーで使用
以下が関連するコントローラーのソースです。（TypeScriptを使っています）

```typescript
//@ts-ignore
import * as moment from "moment"; 
import "moment-timezone";

export default class List extends Controller {
    ...
    public formatTimestamp(timestamp: string): string {
        const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
        const formattedDate = moment.tz(timestamp, timeZone).format("YYYY-MM-DD HH:mm:ss");
        return formattedDate;
    }

}
```

## Mobile Startでの利用
UI5アプリを作成したらビルド、デプロイしてMobile Startで使えるようにします。設定方法は以下のチュートリアルが参考になります。

https://developers.sap.com/tutorials/mobile-start-access-launchpad-site.html