UI5アプリからGoogle Driveにファイルをアップロードする

## はじめに
この記事では、[前回](https://qiita.com/tami/items/272bd74c97919293d590)の記事で作成したCloud Integration (CPI) -> Open Connector -> Google Driveの連携をUI5のアプリケーションから呼び出します。
※シリーズ先頭の記事は[こちら](https://qiita.com/tami/items/9fd12cbdeef81bed3984)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/25918702-81f8-84e4-9caa-320632cd78c5.png)

メインテーマはUI5アプリケーションとCPIとの連携、サブテーマはUI5でType Scriptを使用することです。
作成したソースは[GitHub](https://github.com/miyasuta/file2googledrive)から参照できます。

※UI5でのType Scriptの使用については、過去に以下の記事を書いています。

https://qiita.com/tami/items/fa703b212982a094b7f5

## ステップ
1. CPIのDestinationを登録
1. UI5プロジェクトを作成
1. ファイルアップロード機能の実装
1. Cloud Foundryにデプロイ
1. Cloud Foundryから実行

### 1. CPIのDestinationを登録
Destinationの登録に必要な情報は、Process Integration（プラン：integration-flow）のインスタンスのサービスキーから取得します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/b45feb2d-cf89-59d5-ef53-dfc9a4043e20.png)

以下のDestinationを登録します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/fea4908f-8aad-8c9c-7c7a-65dd0f6eb85d.png)

|項目  |設定値  |
|---|---|
|Name  |任意  |
|Type  |HTTP  |
|URL  |サービスキーのURL  |
|Client ID  |サービスキーのclientid  |
|Client Secret  |サービスキーのclientsecret  |
|Token Service URL  |サービスキーのtokenurl |

### 2. UI5プロジェクトを作成
Type Scriptを使用するため、easy-ui5の[ts-app](https://github.com/ui5-community/generator-ui5-ts-app)のテンプレートを使用します。
以下のコマンドを実行します。

```
yo easy-ui5 ts-app
```
質問には以下のように答えます。
```
? How do you want to name this application? file2googledrive
? Which namespace do you want to use? miyasuta
? Which framework do you want to use? SAPUI5
? Which framework version do you want to use? 1.108.0
? Who is the author of the application? Mio Yasutake
? Would you like to create a new directory for the application? Yes
? Would you like to initialize a local github repository for the application? Yes
```

プロジェクトが生成されたら、`npm start`で実行します。
以下の画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/372a42b9-e040-8c6c-12cb-3264a886f08b.png)

### 3. ファイルアップロード機能の実装
[FileUploader](https://sapui5.hana.ondemand.com/#/api/sap.ui.unified.FileUploader)というコントロールを使ってファイルをアップロードします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/f6ba5121-17c2-f134-e6b2-42f6b8fb7fd5.png)

#### 3.1. ビュー
FileUploaderはデフォルトではリクエストをmultipart/form-dataで送信します。今回使用するCPIのiFlowはファイルをFormDataではなくBodyに受け取るので、`useMultipart="false"`を指定します。これに伴い、`sendXHR="true"`を設定する必要があります。

```xml
<mvc:View
	controllerName="miyasuta.file2googledrive.controller.Main"
	displayBlock="true"
	xmlns="sap.m"
	xmlns:l="sap.ui.layout"
	xmlns:u="sap.ui.unified"
	xmlns:mvc="sap.ui.core.mvc">

	<l:VerticalLayout>
		<u:FileUploader
			id="fileUploader"
			useMultipart="false"
			sendXHR="true"
			uploadUrl="cpi/http/googleDriveIntegration"
			uploadComplete="handleUploadComplete"/>
		<Button
			text="Upload File"
			press="handleUploadPress"/>
	</l:VerticalLayout>

</mvc:View>
```

#### 3.1. コントローラー
コントローラーには2つのメソッドを定義します。
**①handleUploadPress**
"Upload File"ボタンが押されたときに動くイベントハンドラです。iFlowがヘッダにファイル名(filename)を受け取る仕様になっているため、ファイルを送信する前にヘッダパラメータを設定しています。ヘッダパラメータの設定は`sendXHR="true"`を指定した場合のみ可能となっています。

**②handleUploadComplete**
アップロードが完了したときに動くイベントハンドラです。ステータスコードを見て成功/失敗を判断してメッセージを返します。

```javascript
import BaseController from "./BaseController";
import formatter from "../model/formatter";
import FileUploader from "sap/ui/unified/FileUploader";
import FileUploaderParameter from "sap/ui/unified/FileUploaderParameter";
import Event from "sap/ui/base/Event";
import MessageToast from "sap/m/MessageToast";


/**
 * @namespace miyasuta.file2googledrive.controller
 */
export default class Main extends BaseController {
	private formatter = formatter;

	public async handleUploadPress(): Promise<void> {
		const oFileUploader = this.byId("fileUploader") as FileUploader;
		await oFileUploader.checkFileReadable();

		//set headers
		oFileUploader.removeAllHeaderParameters();
		const fileName = oFileUploader.getValue();
		oFileUploader.addHeaderParameter(new FileUploaderParameter({
			name: "filename",
			value: fileName
		}));

		oFileUploader.upload();
	}

	public handleUploadComplete(oEvent: Event): void {
		const iHttpStatusCode = oEvent.getParameter("status") as number
		let	sMessage: string;

		if (iHttpStatusCode) {
			sMessage = iHttpStatusCode === 200 ? "Upload Success" : "Upload Error";
			MessageToast.show(sMessage);
		}
	}

}
```

#### 3.3. xs-app.json
BTPのDestinationを使用してCPIのiFlowを呼びだすため、xs-app.jsonをwebappフォルダの直下に作成します。今回呼び出すiFlowはCSRFトークンを受け取らない設定になっているので、`"csrfProtection": false`を指定します。

```json
{
    "welcomeFile": "/index.html",
    "routes": [ 
      {
          "source": "^/cpi/(.*)$",
          "target": "/$1",
          "destination": "CPI",
          "authenticationType": "xsuaa",
          "csrfProtection": false
      },
      {
        "source": "^(.*)",
        "target": "$1",
        "authenticationType": "xsuaa",
        "service": "html5-apps-repo-rt"
      }
    ]
  }
```


### 4. Cloud Foundryにデプロイ
以下の記事に従ってデプロイ用の設定を追加し、デプロイします。

https://qiita.com/tami/items/fa703b212982a094b7f5#3-btp%E3%81%AB%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3

### 5. Cloud Foundryから実行
デプロイするとHTML5 Applicationsからアプリを実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/a224902d-478c-58db-065d-008340a664d4.png)
ファイルを選択してアップロードすると、"Upload Success"というメッセージが出力されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/70f6f549-1dac-644c-607d-eabb7d7ce4b7.png)
Google Drive側にファイルが連携されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/4d22aff1-b446-c0d6-0318-d4b26b4dfccd.png)

ネットワークタブで確認すると、レスポンスタイムが6.44秒と、なぜか[Postmanから実行したとき](https://qiita.com/tami/items/272bd74c97919293d590#3-%E3%83%86%E3%82%B9%E3%83%88)（約13秒）と比べてだいぶ速くなっていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/ac7deadf-146a-de3c-2471-87a25bae60e2.png)