YellowfinでJavascriptだけでダッシュボードのレポートをすべてExcelでダウンロードする

![excelDLWithoutJsp.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223443/a8982313-0edd-ceae-3660-29a8ad35c019.gif)


#概要
Yellowfinのバージョン9ではダッシュボードのExportはPDFしかできないこともあり、なんとかできないかというリクエストから色々調べて実現できたところのメモです。
前提としてYellowfinはwebserviceのREST化を進めていますが、レポート周りの機能はまだxmlベースのSOAPのやり取りでレガシー過ぎて苦労しました。。
調べたことのそれぞれは下記になります。
[今更ながらJavascriptでSOAPを通してxmlをparseする](https://qiita.com/celery/items/dedf9b4e02d0a10945cd)
[Javascriptでbase64エンコード化されたExcelファイルをダウンロードする](https://qiita.com/celery/items/7a5952c9806630b4848b)
[JavascriptのFetchをネストして2回リクエストを投げる](https://qiita.com/celery/items/f5390fef6163e6dc352c)

前回の全Excelダウンロードとの違いはレポート名をファイル名につけれる点、JSPファイルを使用せずにJavascriptだけで完結できる点です。
[YellowfinのAPIとwebserviceを使ってダッシュボードのレポートを全てExcelでダウンロードする(jspファイル使用)](https://qiita.com/celery/items/83c5cede3e0555e9bb42)

#前準備
今回は2つの前準備があります。
##ボタンwidgetの設置
前回と同じようにダッシュボードを作成後に、コードwidgetからボタンのwidgetをドラッグ・アンド・ドロップして名前をつけます。例)export
これは次のJSタブに記述する部分で使います。ボタンのサイズやテキストは適宜変更してください(ここではExport Report)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223443/fb24dd06-c39d-4262-38fd-d8c54c82a815.png)

##web.xmlの編集からの再起動
※また、今回はリポジトリに直接SQLクエリを投げるwebserviceを使うので[このページのSQLクエリー関数METADATAQUERYのクエリーwebサービスの有効化](https://wiki.yellowfin.co.jp/pages/viewpage.action?pageId=2294012)を参考に**web.xmlを編集して再起動してください。**


#JSタブのコード
少し長くなりますが、これはXMLを全て記述しているからに他ならないですね。。
まず、前準備で用意したボタンのクリックeventlistenerでdashboardAPIを呼び出し、このダッシュボードで使われている全てのレポートの情報を配列で取得します。それをforeachで回しつつreportUUIDを取得して管理サービスのリポジトリに直接SQLを投げるwebserviceを使います。※前準備のweb.xml変更を参照
これでレポートの内部reportidを取得できるので、xmlをparseしてレポートサービスへfetchのネストでリクエストを送ります。return doc.getElementsByTagName("dataValue")[1].textContent;の部分がreportidを取得してるところですね。
そこからはほぼこの2ページと同じです。
[今更ながらJavascriptでSOAPを通してxmlをparseする](https://qiita.com/celery/items/dedf9b4e02d0a10945cd)
[Javascriptでbase64エンコード化されたExcelファイルをダウンロードする](https://qiita.com/celery/items/7a5952c9806630b4848b)

```javascript:ExcelExportAll.js
// ここに変数を宣言したり、コードを記述します

/**
 * キャンバスとそのすべての子要素をページにレンダリングしたりアタッチするときに呼び出されます。
 */
this.onRender = function () {
    // ここにコードを記述します。これは、イベントリスナーを設定するのに理想的な場所です
    let button = this.apis.canvas.select('export');
    button.addEventListener('click', () => {
        var dash = this.apis.dashboard;
        var allrep = dash.getAllReports();
        allrep.forEach( item => {
            var uuid = item.reportUUID;
            fetch('http://localhost:8922/services/AdministrationService', {
                method: 'POST',
                body: '<?xml version="1.0" encoding="utf-8"?>' +
                '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://webservices.web.mi.hof.com/">' +
                '<soapenv:Header/>' +
                '<soapenv:Body>' +
                '<web:remoteAdministrationCall>' +
                '<arg0>' +
                '<loginId>admin@yellowfin.com.au</loginId>' +
                '<password>test</password>' +
                '<orgId>1</orgId>' +
                '<function>METADATASQLQUERY</function>' +
                "<query>SELECT reportid FROM reportheader where reportstatuscode = 'OPEN' and publishuuid = '" + uuid + "'</query>" +
                '</arg0>' +
                '</web:remoteAdministrationCall>' +
                '</soapenv:Body>' +
                '</soapenv:Envelope>',
                headers: {
                    'Content-Type': 'text/xml',
                    'SOAPAction': ''
                },
            }).then(function(response) {
                return response.text();
            }).then(function(responseText) {
                var parser = new DOMParser();
                var doc = parser.parseFromString(responseText, "text/xml");
                return doc.getElementsByTagName("dataValue")[1].textContent;
            }).then(function(reportId) {
                return fetch('http://localhost:8922/services/ReportService', {
                    method: 'POST',
                    body: '<?xml version="1.0" encoding="utf-8"?>' +
                    '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://webservices.web.mi.hof.com/">' +
                    '<soapenv:Header/>' +
                    '<soapenv:Body>' +
                    '<web:remoteReportCall>' +
                    '<arg0>' +
                    '<loginId>admin@yellowfin.com.au</loginId>' +
                    '<password>test</password>' +
                    '<orgId>1</orgId>' +
                    '<reportRequest>XLSX</reportRequest>' +
                    '<reportId>' + reportId + '</reportId>' +
                    '</arg0>' +
                    '</web:remoteReportCall>' +
                    '</soapenv:Body>' +
                    '</soapenv:Envelope>',
                    headers: {
                        'Content-Type': 'text/xml',
                        'SOAPAction': ''
                    },
                });
            }).then(function(response) {
                return response.text();
            }).then(function(responseText) {
                var parser = new DOMParser();
                var doc = parser.parseFromString(responseText, "text/xml");
                var reportName = doc.getElementsByTagName("reportName")[0].textContent;
                var binary = doc.getElementsByTagName("binaryData")[0].textContent;
                binary = atob(binary);
                var decoded_array = new Uint8Array(Array.prototype.map.call(binary, c => c.charCodeAt()));
                var decoded = new Blob([decoded_array], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"});
                var url = window.URL.createObjectURL(decoded);
                var a = document.createElement("a");
                a.href = url;
                a.download = reportName + ".xlsx";
                a.click();
                window.URL.revokeObjectURL(url);
            }).catch(function(e) {
                // エラーが発生した場合の処理
                console.log(e);
            }); 
        });
    });
};

/**
 * キャンバスとそのすべての子要素がページから削除されるときに呼び出されます。
 */
this.onRemove = function () {
    // ここにコードを記述します。これは、イベントリスナーを削除するのに理想的な場所です
};
```
#まとめ
これでJSタブに記載するだけで(長い)個別ですがダッシュボード中の全てのレポートのExcelダウンロードが可能になりました。。
これにダッシュボードのフィルター適用後の値が適用できたらいいのですがJSPファイルを使わないと難しそうで、さらにできるかどうかは確認中のようです。。。