【intra-mart】[IM-FormaDesigner]別のFormaアプリで添付したファイルをダウンロードURL付きで取得する方法

今日の時点でもう期間が過ぎてしまっているのですが、はじめてAdvent Calendarの記事を投稿します。

* NTTデータイントラマート Advent Calendar 2020

### やりたかったこと

あるFormaアプリ(A)でアップロードしたファイル一覧を、別のFormaアプリ(B)のグリッドテーブルでダウンロードしたり、選択したりしたかったのです。
JavaEE開発モデルやスクリプト開発モデルを用いて専用の画面を作成すれば、すぐに実現可能と思われるのですが、それだとシステム停止を伴うデプロイ作業が発生してしまいます。システム停止調整となると・・・社内事情やユーザ事情などいろんなことがあり、それだけは避けなければならない・・・。
したがってIM-FormaDesignerなど、デプロイを伴わない機能だけで要件を満たし、迅速に対応する必要がありました。

* IM-FormaアプリAで添付ファイルをアップロード
* IM-FormaアプリB側で、A側のファイルを取得し、ダウンロードURLを生成
* これをJava開発モデルやスクリプト開発モデルではなく、warファイル差し替えを行わずデプロイを伴わない下記の方法で実現したい
    * IM-FormaDesigner
    * IM-LogicDesigner
    * データソース定義


### 概要図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93122/5eeef808-7560-e218-add0-fa98ae9f871f.png)


### 成果物（gifアニメーション）

![get_other_app_file.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93122/ffb9b517-a157-bf43-3244-eca7687e5518.gif)


### 成果物の各種定義

* 下記よりダウンロードできます
    * [各種定義_rngd_sample_02.zip](http://www.rinsymbol.sakura.ne.jp/intra-mart/002_get_other_file_dl_url/各種定義_rngd_sample_02.zip)

http://www.rinsymbol.sakura.ne.jp/intra-mart/002_get_other_file_dl_url/各種定義_rngd_sample_02.zip


### やったこと

#### ダウンロード用ページの解析

IM-FormaDesignerからファイルをダウンロードするには、独自のダウンロード用ページを経由するか、
標準のファイルダウンロード部品のページを経由する必要があります。

独自のダウンロード用ページは前述したとおりデプロイが必要となるので利用ができません。
そこで、標準のファイルダウンロードページを経由させることにします。

標準のファイルダウンロードページ：```forma/designer/types/product/72/fileupload/download```


しかし、ここに然るべきパラメータを```file_id```や```application_id```を渡すだけでは適切に動作しません。
```im_mark```という（おそらく）セッションごとに自動付与されるパラメータを渡してあげる必要があります。
```im_mark```はどうやったら取得できるのか？これを調べる必要があります。


intra-mart社が作成したソースが格納されているテナントID/WEB-INF配下をdownloadなどのキーワードでgrep検索してみます。
そうすると、```WEB-INF/jssp/product/src/forma/designer/types/product/72/fileupload```配下にいい感じのコードが見つかります。

* fileupload_savefile.jsの48行目付近
 
```javascript:fileupload_savefile.js
    var downloadUrl = new URL('forma/designer/types/product/72/fileupload/download');
    downloadUrl.setArgument('application_id', request.applicationId);
    downloadUrl.setArgument('insert_id', request.insertId);
    downloadUrl.setArgument('upload_item_id', request.uploadItemId);
    downloadUrl.setArgument('imfr_process_key', request.processKey);
    downloadUrl.setArgument('file_id', FRCryptionUtil.encrypt(result.fileId));
    downloadUrl.setArgument('type', result.fileInfo.type);

    // ファイル情報の返却
    response.sendMessageBodyString(ImJson.toJSONString([{
      name           : oFile.getFileName(),
      application_id : request.applicationId,
      insert_id      : request.insertId,
      process_key    : request.processKey,
      input_id       : request.uploadItemId,
      file           : {
        id           : result.fileId,
        encrypt_id   : FRCryptionUtil.encrypt(result.fileId),
        type         : result.fileInfo.type,
        size         : result.fileInfo.file_size,
        name         : oFile.getFileName(),
        display_size : displaySize(result.fileInfo.file_size),
        notes        : request.fileNotes,
        record_date  : Procedure.imfr_date_utils.getDisplayDatetimeFormat(recordDate,
          formats.IM_DATETIME_FORMAT_DATE_STANDARD, formats.locale),
        record_time  : Procedure.imfr_date_utils.getDisplayDatetimeFormat(recordDate,
          formats.IM_DATETIME_FORMAT_DATE_STANDARD + ' ' + formats.IM_DATETIME_FORMAT_TIME_STANDARD, formats.locale),
        download_url : downloadUrl.location(),
        imageThumbnailPreview : isImageThumbnailPreview
      }
    }]));
```

どうやら、URLクラスオブジェクトに然るべきパラメータを渡すことでダウンロード用のURLを生成しているようです。
そこで、このソースを元に、LogicDesignerのJavaScript定義を作成し、IN/OUTを設定します。

#### ロジックフローの作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93122/522c577e-85fb-b0bc-d690-ab7d6569ec95.png)


```javascript
function run(input) {

  var downloadUrl = null;
  var downloadUrlStr = "";
  var downloadUrlForATag = "";
  var errorFlg = false;
  var errorMsg = "";
  
  try {
    downloadUrl = new URL('forma/designer/types/product/72/fileupload/download');
    downloadUrl.setArgument('application_id', input.applicationId);
    downloadUrl.setArgument('insert_id', input.insertId);
    downloadUrl.setArgument('upload_item_id', input.uploadItemId);
    downloadUrl.setArgument('imfr_process_key', input.processKey);
    downloadUrl.setArgument('file_id', FRCryptionUtil.encrypt(input.fileId));
    downloadUrl.setArgument('type', input.type);
  } catch(e) {
    errorFlg = true;
    errorMsg = e.message;
  } finally {
    if(!errorFlg) {
      downloadUrlStr = downloadUrl.location();
      downloadUrlForATag = "<a href='" + downloadUrlStr + "'>" + input.fileName + "</a>";
    }
  }

	return {
		downloadUrl: downloadUrlStr,
		errorFlg: errorFlg,
		errorMsg: errorMsg,
		downloadUrlSource: downloadUrlForATag
	};
}
```

これでダウンロードURL作成の準備が完了しました。

あとは、LogicDesignerで下記のような処理を実装し、データソース定義経由で画面に返してあげればOKです

* SQL定義を用いて```IMFR_T_FILEUPLOAD```テーブルから、トランザクションの```INSERT_ID```と```APPLICATION_ID```を元にファイルIDやファイル名を取得する
* 繰り返しタスクで、上記結果オブジェクトをループ
    * 各ファイルIDやファイル名ごとに、ダウンロードURL作成用のJavaScriptを実行し、URLを取得。データを返却
    * データをリスト形式でpushしていく
* リスト形式のデータを画面に返却する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93122/61f3ce72-cc0a-c053-2a84-f894181c2a46.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93122/400091e7-3953-3e50-b651-1ac4ea263476.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93122/df16e139-7730-c2bb-ecdf-9db00423d38c.png)

### 画面イメージ

* 別アプリでファイル添付
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93122/c1e3e8cd-7cad-9687-dac2-52f39c939cd3.png)


* 初期表示
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93122/1af14507-5d03-2f8a-5396-e32d8951ff47.png)

* 別アプリのデータを検索
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93122/53cfc6ea-b53b-1c8c-5a77-33a1438084b5.png)

* 別アプリでアップロードしたファイルと、ダウンロード用URLが表示
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93122/ab20d7d5-f37e-df25-fd81-290187a61a6a.png)


これで、システム停止を行わず（デプロイせず）に、IM-FormaDesigner, LogicDesignerのみで対応を行うことができました。

年末にこの課題対応ができて一安心です。

### 最後に注意点

本件はあくまでもデプロイを伴うシステム変更が困難である場合の奥の手です。
本当にこの内容で対応を実施すべきかどうかを熟考した上で適用すべきと考えます。
申し訳ありませんが、この対応によって発生した障害等について、一切責任を負いませんので、ご了承ください。