YellowfinのAPIとwebserviceを使ってダッシュボードのレポートを全てExcelでダウンロードする(jspファイル使用)

![ExcelDL.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223443/9c54b340-3247-ac42-4c20-e358179ba816.gif)

#やりたいこと
またまたタイトル通りなのですが、前回の記事[FetchでExcelファイルがダウンロードできるページに条件をPOSTしてブラウザダウンロードする](https://qiita.com/celery/items/36607b7ea0e2382add32)でExcelのダウンロードをできるようになったので、Yellowfinのダッシュボードにある全てのレポートをExcelでダウンロードしたいってところです。
検証しているのはversion9.2.2ではBaseAPI,reportAPI,filterAPI,DashboardAPIとコードモードでいろいろなことができるようになっているので、そこで各レポートの情報を取得し、foreachで回す感じです。

##前準備をします
ダッシュボードを作成後に、コードwidgetからボタンのwidgetをドラッグ・アンド・ドロップして名前をつけます。例)export
これは次のJSタブに記述する部分で使います。

また、Excelをエクスポートするwebservice用スクリプトを/Yellowfinインストールディレクトリ/appserver/webapps/ROOT/の下に配置します。

これはExcel以外にも対応してるのでPOSTするキーのformatを他のPDF・CSVに変更してもformatをそれぞれに合わせれば使用できます。
また何もしてしなければPDFファイルで出力されます。

```java:output.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" %>
<%@ page import="java.util.*, java.text.*" %>
<%@ page import="com.hof.mi.web.service.*" %>
<%@ page import="java.net.URLEncoder" %>
<%
  String host = "localhost";
  Integer port = 8080;
  String userid = "admin@yellowfin.com.au";
  String password = "test";
  String orgid = "1";
  String uuid = request.getParameter("uuid");
  String fname = request.getParameter("fname");
  String path = "/services/ReportService";
  String suffix = "";

  String format = request.getParameter("format");
  String contentType = "application/octet-stream";
  if (format == null) format = "PDF";
  if (format.equals("CSV")) {
    contentType = "text/comma-separated-values";
    suffix = ".csv";
  }
  else if (format.equals("PDF")) {
    contentType = "application/pdf";
    suffix = ".pdf";
  }
  else if (format.equals("XLS")) contentType = "application/vnd.ms-excel";
  else if (format.equals("XLSX")) {
    contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
    suffix = ".xlsx";
  }
  else if (format.equals("RTF")) contentType = "application/rtf";
  else if (format.equals("TEXT")) contentType = "text/tab-separated-values";

  HashMap filters = new HashMap();
  boolean cleared = filters.size() == 0;
  Iterator f = request.getParameterMap().keySet().iterator();
  while (f.hasNext()) {
    String key = (String) f.next();
    if (key.startsWith("filter")) {
      if (!cleared) {
        filters.clear();
        cleared = true;
      }
      String value = request.getParameter(key);
      int pipeIndex = value.indexOf("|");
      if (pipeIndex == -1) continue;
      key = value.substring(0, pipeIndex);
      value = value.substring(pipeIndex + 1);
      filters.put(key, value);
    }
  }

  ReportServiceClient rsc = new ReportServiceClient(host, port, userid, password, path);
  i4Report report = rsc.loadReportForUser(uuid, userid, password, orgid);

  f = filters.keySet().iterator();
  while (f.hasNext()) {
    String key = (String) f.next();
    String value = (String) filters.get(key);
    report.setFilter(key, value);
  }

  HashMap elementStorage = new HashMap();
  report.run(elementStorage, format);

  response.setContentType(contentType);
  fname = fname + suffix;

  String encodedFilename = URLEncoder.encode( fname , "UTF-8");
  response.setHeader("Content-Disposition","attachment;" +
  "filename=\"" + encodedFilename + "\"");
  
  
  java.io.BufferedOutputStream o = new java.io.BufferedOutputStream(response.getOutputStream(), 32000);
  o.write(report.renderBinary());
  o.flush();

%>
```

##ダッシュボードのJSタブ
ここに前回やった部分を少し改造して全てのレポートをエクスポートできるようにDashboardAPIを駆使します。
let button = this.apis.canvas.select('export');の部分でダッシュボード上に設置したボタンの名前を指定することでこれをeventlistenerで取得できます。
他の[前回の記事](https://qiita.com/celery/items/36607b7ea0e2382add32)で解説していないこととしては、こちらの部分です。
        var dash = this.apis.dashboard;
        var allrep = dash.getAllReports();
これはYellowfinのdashboardAPIを呼び出し、getAllReports()でダッシュボード上のレポート情報を全て取得しています。
[該当のYellowfinのwiki部分](http://wiki.yellowfinbi.jp/pages/viewpage.action?pageId=6591558#id-%E3%83%80%E3%83%83%E3%82%B7%E3%83%A5%E3%83%9C%E3%83%BC%E3%83%89API-%E3%83%AC%E3%83%9D%E3%83%BC%E3%83%88%E3%81%AE%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9)
それぞれのレポートをfoeachで回し、uuidとformatをbodyで渡してリンクを生成させてます。

レポート名も取得したい場合はさらにwebservice用のjsp作らないといけないのでまた次の機会に。


```javascript: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;
            var obj = {
                fname: uuid,
                uuid: uuid,
                format: 'XLSX'
                };
            var method = "POST";
            var body = Object.keys(obj).map((key)=>key+"="+encodeURIComponent(obj[key])).join("&");
            var headers = {
              'Accept': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
              'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
              'responseType' : "blob",
            };
            fetch("./output_file.jsp", {method, headers, body})
            .then((res)=> res.blob())
            .then(blob => {
                let anchor = document.createElement("a");
                anchor.href = window.URL.createObjectURL(blob);
                anchor.download = uuid+".xlsx";
                anchor.click();})
            .then(console.log)
            .catch(console.error);
        });
    });
};


```

#結果
ダッシュボードに設置しているレポートがどんどん落ちてきます。たぶん初回はブラウザ側でダウンロードの許可を求めるダイアログが出るので許可してあげれば大丈夫です。
※今の所レポートにダッシュボードで使用していたフィルターの内容を渡せないようなので、これがやりたい場合は、動的ではなく予め条件毎にダッシュボードを作っておく必要があります。。。この辺がなんとかなればかなり使えると思うんですけどね。