新型コロナウイルス(COVID-19)の Lightning Web Component を作ってみた感触



<div align="center">
<img src="https://raw.githubusercontent.com/arufian/LWC-Component-COVID19/master/screenshots/contact.png" alt="属性" title="タイトル">
</div>

## 背景

最近Lightning Web Component（LWC）を触ってみて、実践的な開発経験をしてみたいと思ってました。そして現在新型コロナウイルスが広がり、何か自分でも社会貢献にできることを考えました。

## アイデア
Salesforceオブジェクトのレコードページ内にレコードの国名に関連する感染者情報を表示させたい。更に、感染者情報のトレンドも一目でわかるようにグラフで表現させたい。

## やり方

### 国名データ抽出
そもそも国名の情報を持ってるSalesforceオブジェクトは取引先と取引先責任者のみなので、これらのオブジェクト限定で対応する形になります。デベロッパーエディションなどで、サンプルデータをみてみたら取引先オブジェクトの中に請求先の国と市区郡（BillingCountryと BillingCity）が一番記入されましたし、そして取引責任者は郵送先の国と市区郡（MailingCountryとMailingCity）が一番多かったと見てましたので、「BillingCountry、BillingCity、MailingCountryとMailingCity」を元に国名のデータを抽出します。

### COVID-19データソース
色々調査し、比較した結果今回こちらの[データーソース](https://pomber.github.io/covid19/timeseries.json)を利用してます。毎日３回アップデートされるので、最新データを取得できるかと思います。

### グラフ描画
[ChartJS](https://www.chartjs.org/)を利用します。こちらの[公式記事](https://developer.salesforce.com/blogs/developer-relations/2017/03/building-lightning-components-chart-js.html)にも書いてありますので、LWCでグラフを描画させるのはChartJSが一番最適です。

## ロジックと流れ
こんな感じでロジックと流れを考えました。

<div align="center">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262/53dfacba-4a2c-6549-7526-205f10a0830c.png" alt="属性" title="タイトル">
</div>


## アーキテクチャー

データソースはAPIではなくJSONファイルのため、LWCに渡る前に事前処理をしないといけません。そのために、中間APIが必要です。中間APIは事前処理のためだけではなく、キャッシュをさせるためもできます。それを実現したら、こんな感じになります。
![architechture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2262/18c3b989-f78e-0d48-becb-d11e44e56e51.png)

## 直面した課題

### オブジェクトによりレコードを取得

LWCでレコードを取得するのは[@wire](https://developer.salesforce.com/docs/component-library/documentation/ja-jp/lwc/lwc.data_wire_service_about)で取得する必要があります。しかし`@wire`でのオブジェクトカラムの定義、普通は静的でないといけないので、今回のように取引先オブジェクトかどうかを検出し定義するカラムを決める、という動的な定義はできませんでした。

```javascript

import { LightningElement, api, wire } from 'lwc';
import { getRecord } from 'lightning/uiRecordApi';
import ACCOUNT_NAME_FIELD from '@salesforce/schema/Account.Name';

export default class Record extends LightningElement {
    @api recordId;
    // 静的でアブジェクトカラムを定義
    @wire(getRecord, { recordId: '$recordId', fields: [ACCOUNT_NAME_FIELD] })
    record;
}
```
#### 解決方法 - `$`
`$`プリフィックスはよく`@wire`の中にレコードIDなどに取得するために使用すると思いますが、実はこの記号を付けると変数をリアクティブかつ動的にすることができたと以下の記事に書いております
[wireサービスの公式記事](https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.data_wire_service_about)
つまり、変数を`fields`に入れて値が変わる毎に`wire`を動的に実行するという実装ができました。こんな感じです。

```javascript
import { LightningElement, wire, api } from 'lwc';

export default class Covid19 extends LightningElement {
  @api recordId;
  @api objectApiName;
  fields;

  @wire(getRecord, { recordId: '$recordId', fields: '$fields' })
  load(result) {
    if (result.data) {
      this.record = result.data.fields;
      this.fetchApi()
    }
  }
  async connectedCallback() {
    if (this.objectApiName === 'Account')
      this.fields = [ACCOUNT_BILLING_CITY, ACCOUNT_BILLING_COUNTRY];
    else
      this.fields = [CONTACT_MAILING_CITY, CONTACT_MAILING_COUNTRY];
  }
}
```

後、ご存知ない方も居るかもしれないので、htmlに使用する変数は`@track`必要ありません。こちらの[プルリクエスト](https://github.com/arufian/LWC-Component-COVID19/pull/1)を参考して下さい。

### レコードに国名が含まれない時

必ずやレコードの中に国名が含まれるわけではないですが、JSONでは国名がキーになってます。

#### 解決方法 
[all-the-cities](https://www.npmjs.com/package/all-the-cities)というnpmパッケージを中間APIに導入し、フィルタリングします。コードはこんな感じです。

```javascript
import Cities from 'all-the-cities'

export const searchCountry = (city, flags) => {
  const dataList = Cities.find((cityName) => {
    return cityName.name.toLowerCase().match(city.toLowerCase())
  })
  const countryCode = dataList.country
  const flagsKeys = Object.keys(flags)
  const flagsValues = Object.values(flags)
  const idx = flagsValues.findIndex((country) => {
    return country.code.match(countryCode)
  })
  return flagsKeys[idx]
}
```

### 国名の類義語 （サーバー側課題）
今回中間APIを使って、データソースのJSONリストから該当の国のデータのにを抽出します。しかし、世界には国の名前の呼び方がいくつかあるという国が存在してます。日本では`Japan`ということで完結できましたが、例えば米国とかだと色々な国名があります。`US`や`USA`や`United States`や`United States of America`などでも同じ国を指してます。JSON側は一つしか存在してないので、問題が起こりました。実施にこちらの[issue](https://github.com/arufian/LWC-Component-COVID19/issues/2)にこのような問題が上がりました。

#### 解決方法 - 類義語システム
上記の問題にならないために、中間サーバーに類義語システムを実装しました。例：先ほどの米国とかは`US`や`USA`や`United States`や`United States of America`などを`US`に変換し、JSONからデータを抽出されます。

## ソースコード

完成したソースコードはこちら[Github](https://github.com/arufian/LWC-Component-COVID19)にありますので、どうぞ自由に使ってください。

### フィードバックやプルリクエスト
何かありましたらこちらの[Github Issue](https://github.com/arufian/LWC-Component-COVID19/issues/new)や[Github PR](https://github.com/arufian/LWC-Component-COVID19/compare)を活用してください。

いやあ、久々に書きました。そのためまだ書き慣れてないので、何か訂正があればコメントをお願いします。