お気に入りのダッシュボードを Web Service を介して管理 - 一斉削除と一斉登録

## はじめに
[Yellowfin](https://yellowfin.co.jp/) を長く運用していると、ユーザーのお気に入りのダッシュボード登録に統制が取れなくなってくることもあるかと思います。時には、管理者側で既存のお気に入りコンテンツを棚卸して、全社一斉にお気に入りのコンテンツを差し替えてしまいたいこともあるかと思います。そんな時に利用できる Web Service を紹介します。
様々なコンテンツの取り扱いが可能ですが、本記事ではダッシュボードを例に取って、下記の流れで説明をします。
1. お気に入りダッシュボードの一覧取得
2. お気に入りダッシュボードの一斉削除
3. お気に入りダッシュボードの一斉登録

## 1. お気に入りダッシュボードの一覧取得
まずは、全体でどのようなダッシュボードがお気に入り登録されているかを確認する必要があります。
Web Service 経由でも、お気に入り登録されたコンテンツ一覧の取得は可能です。ただ、Web Service 経由の場合は、ユーザー毎のお気に入りコンテンツを取得することになるため、下記クエリー (PostgreSQL の例) を発行して、リポジトリ DB から直接データを取得してしまった方が早いと思います。

```sql:リポジトリDB
SELECT P.ipperson, F.contentid, concat_ws(' ', P.firstname, P.lastname) as name, R.shortdescription
FROM person as P
INNER JOIN personfavourite AS F ON P.ipperson = F.ipperson
INNER JOIN reportgroup AS R ON F.contentid = R.groupid
WHERE F.contenttypecode = 'REPORTGROUP';
```

結果、以下のような一覧が入手できます。継続の処理で使用するのは、ipperson と contentid の 2 列の情報です。name と shortdescription は参考情報です。
ipperson と contentid は、いずれも内部管理用の id で、整数値 (integer) です。外部からは見えないため、見慣れない方も多いかと思います。

|ipperson|contentid|name|shortdescription|
|---|---|---|---|
|13152|92078|トビー ロバーツ|Analysis|
|13152|61246|トビー ロバーツ|GIS Maps|
|13152|96876|トビー ロバーツ|Administration Tour|
|13152|99929|トビー ロバーツ|Series Selection|
|13150|91910|ステファン カリー|Drill|
|13150|70093|ステファン カリー|Sales Performance|
|13150|99656|ステファン カリー|Campaign Analysis|
|13150|92078|ステファン カリー|Analysis|

## 2. お気に入りダッシュボードの一斉削除
先の手順で取得した一覧情報 (ヘッダー行無し) を、カンマ区切りの CSV ファイルとして、以下のディレクトリに保存します。ちなみに、Yellowfin は豪州発祥のソフトウェアであるため、favorite ではなく favourite です。
`<Yellowfin>/appserver/webapps/ROOT/favourite.csv`
CSV ファイルと同じディレクトリに、下記の JSP ファイルも保存します。
ブラウザを立ち上げて、https://yellowfin-server:8080/removefavourite.jsp にアクセスすると、jsp ファイルが起動し、処理が開始されます。

```jsp:removefavorite.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" %>
<%@ page import="com.hof.util.*" %>
<%@ page import="com.hof.web.form.*" %>
<%@ page import="com.hof.mi.web.service.*" %>
<%@ page import="java.text.*" %>
<%@ page import="java.util.*" %>
<%@ page import="java.io.*" %>

<%
String filepath = "favourite.csv";
String server = "yellowfin-server";
Integer port = 9140;
Integer admin_orgid = 1;
String admin_uid = "admin@yellowfin.com.au";
String admin_password = "test";
String function_type = "REMOVEPERSONFAVOURITE";
String content_type = "REPORTGROUP";

String line = "";
String[] data = null;

ArrayList<String[]> al = new ArrayList<String[]>();

InputStreamReader reader = new InputStreamReader(new FileInputStream(application.getRealPath(filepath)),"UTF-8");
BufferedReader br = new BufferedReader(reader);

while((line = br.readLine()) != null){
    data = line.split(",");
    al.add(data);
}

String[][] arr = new String[al.size()][];
for (int ii = 0; ii < al.size(); ii++) {
    arr[ii] = al.get(ii);
}

for(int i=0; i<arr.length; i++){
    
    AdministrationServiceService s_adm = new AdministrationServiceServiceLocator(server,port, "/services/AdministrationService", false);
    AdministrationServiceSoapBindingStub adminService = (AdministrationServiceSoapBindingStub) s_adm.getAdministrationService();
    AdministrationServiceRequest rsr = new AdministrationServiceRequest();

    rsr.setLoginId(admin_uid);
    rsr.setPassword(admin_password);
    rsr.setOrgId(admin_orgid);
    rsr.setFunction(function_type);

    PersonFavourite pf = new PersonFavourite();
    int person = Integer.parseInt(arr[i][0]);
    int favorite = Integer.parseInt(arr[i][1]);
    pf.setPersonId(person);
    pf.setContentId(favorite);
    pf.setContentType(content_type);
    rsr.setFavourite(pf);

    AdministrationServiceResponse rs = adminService.remoteAdministrationCall(rsr);

    if ("SUCCESS".equals(rs.getStatusCode()) ) {
        out.write("削除成功: "+ arr[i][0] + "," + arr[i][1] + "<br>");
    } else {
        out.write("[警告] : "+ arr[i][0] + "," + arr[i][1]);
        out.write(" -- Error Code: " + rs.getErrorCode() + "<br>");
    }
}
br.close();
%>
```

簡単にコードの内容を説明します。

下記の行では、favourite.csv ファイルからデータを読み込んで、Array List に取り込んだ後、2 次元配列に加工しています。後の処理で必要なデータ型です。
```java:CSV データを 2 次元配列に加工
String filepath = "favourite.csv";
String line = "";
String[] data = null;

ArrayList<String[]> al = new ArrayList<String[]>();

InputStreamReader reader = new InputStreamReader(new FileInputStream(application.getRealPath(filepath)),"UTF-8");
BufferedReader br = new BufferedReader(reader);

while((line = br.readLine()) != null){
    data = line.split(",");
    al.add(data);
}

String[][] arr = new String[al.size()][];
for (int ii = 0; ii < al.size(); ii++) {
    arr[ii] = al.get(ii);
}
```

結果、以下のようなデータが出来上がります。
```cmake:2 次元配列
[13152, 92078, "トビー ロバーツ", "Analysis"]
[13152, 61246, "トビー ロバーツ", "GIS Maps"]
[13152, 96876, "トビー ロバーツ", "Administration Tour"]
[13152, 99929, "トビー ロバーツ", "Series Selection"]
[13150, 91910, "ステファン カリー", "Drill"]
[13150, 70093, "ステファン カリー", "Sales Performance"]
[13150, 99656, "ステファン カリー", "Campaign Analysis"]
[13150, 92078, "ステファン カリー", "Analysis"]
```

以下のコードでは、先のコードで作成した 2 次元配列を使って、順に該当するコンテンツを、お気に入りから削除します。
server や port で指定する情報は、各社の環境に合わせて変更してください。
admin_uid で指定するユーザーの権限で処理を行うため、システム管理者権限のあるユーザーを指定します。
function_type で REMOVEPERSONFAVOURITE を指定すると、お気に入りからの削除になります。また、content_type で REPORTGROUP を指定すると、ダッシュボードが処理対象となります。それ以外の処理種別に関しては、[Yellowfin Wiki](https://wiki.yellowfin.co.jp/space/yfcurrent/1901015/%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E5%8F%96%E5%BE%97+&+%E6%93%8D%E4%BD%9C%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9) をご確認ください。
最後に、処理の成功、失敗に応じたメッセージをブラウザに表示します。


```java:まとめて削除
String server = "yellowfin-server";
Integer port = 8080;
Integer admin_orgid = 1;
String admin_uid = "admin@yellowfin.com.au";
String admin_password = "test";
String function_type = "REMOVEPERSONFAVOURITE";
String content_type = "REPORTGROUP";

for(int i=0; i<arr.length; i++){
	
	AdministrationServiceService s_adm = new AdministrationServiceServiceLocator(server,port, "/services/AdministrationService", false);
	AdministrationServiceSoapBindingStub adminService = (AdministrationServiceSoapBindingStub) s_adm.getAdministrationService();
	AdministrationServiceRequest rsr = new AdministrationServiceRequest();

	rsr.setLoginId(admin_uid);
	rsr.setPassword(admin_password);
	rsr.setOrgId(admin_orgid);
	rsr.setFunction(function_type);

	PersonFavourite pf = new PersonFavourite();
	int person = Integer.parseInt(arr[i][0]);
	int favorite = Integer.parseInt(arr[i][1]);
	pf.setPersonId(person);
	pf.setContentId(favorite);
	pf.setContentType(content_type);
	rsr.setFavourite(pf);

	AdministrationServiceResponse rs = adminService.remoteAdministrationCall(rsr);

	if ("SUCCESS".equals(rs.getStatusCode()) ) {
		out.write("削除成功: "+ arr[i][0] + "," + arr[i][1] + "<br>");
	} else {
		out.write("[警告] : "+ arr[i][0] + "," + arr[i][1]);
		out.write(" -- Error Code: " + rs.getErrorCode() + "<br>");
	}

}

br.close();
```

## 3. お気に入りダッシュボードの一斉登録
続いて、まとめてお気に入りのダッシュボードを登録する方法です。
下記のコードを、先ほどの削除プログラムと同じディレクトリに保管します。
`<Yellowfin>/appserver/webapps/ROOT/savefavourite.jsp`
登録する一覧情報は、先ほどと同様に favourite.csv とします。
ブラウザを立ち上げて、https://yellowfin-server:8080/savefavourite.jsp にアクセスすると、jsp ファイルが起動し、処理が開始されます。

```jsp:savefavorite.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" %>
<%@ page import="com.hof.util.*" %>
<%@ page import="com.hof.web.form.*" %>
<%@ page import="com.hof.mi.web.service.*" %>
<%@ page import="java.text.*" %>
<%@ page import="java.util.*" %>
<%@ page import="java.io.*" %>

<%
String filepath = "favourite.csv";
String server = "yellowfin-server";
Integer port = 8080;
Integer admin_orgid = 1;
String admin_uid = "admin@yellowfin.com.au";
String admin_password = "test";
String function_type = "SAVEPERSONFAVOURITE";
String content_type = "REPORTGROUP";
String favorite_type = "REPORTDASHBOARD";

String line = "";
String[] data = null;

ArrayList<String[]> al = new ArrayList<String[]>();

InputStreamReader reader = new InputStreamReader(new FileInputStream(application.getRealPath(filepath)),"UTF-8");
BufferedReader br = new BufferedReader(reader);

while((line = br.readLine()) != null){
	data = line.split(",");
	al.add(data);
}

String[][] arr = new String[al.size()][];
for (int ii = 0; ii < al.size(); ii++) {
	arr[ii] = al.get(ii);
}

for(int i=0; i<arr.length; i++){
	
	AdministrationServiceService s_adm = new AdministrationServiceServiceLocator(server,port, "/services/AdministrationService", false);
	AdministrationServiceSoapBindingStub adminService = (AdministrationServiceSoapBindingStub) s_adm.getAdministrationService();
	AdministrationServiceRequest rsr = new AdministrationServiceRequest();

	rsr.setLoginId(admin_uid);
	rsr.setPassword(admin_password);
	rsr.setOrgId(admin_orgid);
	rsr.setFunction(function_type);

	PersonFavourite pf = new PersonFavourite();
	int person = Integer.parseInt(arr[i][0]);
	int favorite = Integer.parseInt(arr[i][1]);
	pf.setPersonId(person);
	pf.setContentId(favorite);
	pf.setContentType(content_type);
	pf.setFavouriteType(favorite_type);
	rsr.setFavourite(pf);

	AdministrationServiceResponse rs = adminService.remoteAdministrationCall(rsr);

	if ("SUCCESS".equals(rs.getStatusCode()) ) {
		out.write("登録成功: "+ arr[i][0] + "," + arr[i][1] + "<br>");
	} else {
		out.write("[警告] : "+ arr[i][0] + "," + arr[i][1]);
		out.write(" -- Error Code: " + rs.getErrorCode() + "<br>");
	}

}

br.close();

%>
```

先ほどの削除プログラムと異なる点は、以下の 2 点のみです。
function_type に SAVEPERSONFAVOURITE を指定し、登録処理を行います。
favorite_type に REPORTDASHBOARD を指定して、登録するコンテンツがダッシュボードであることを明示します。

```java:登録処理
String function_type = "SAVEPERSONFAVOURITE";
String favorite_type = "REPORTDASHBOARD";
rsr.setFunction(function_type);
pf.setFavouriteType(favorite_type);
```

## 最後に
REST API でも、Yellowfin のコンテンツを操作する機能は実装されています。
しかし、今のところ、管理者権限で全ユーザーのお気に入りコンテンツを削除・登録する機能は、REST API には実装されていません。現状、REST API 経由では、各ユーザーは、自分自身のお気に入りコンテンツしか削除・登録することができません。
今後、REST API 経由でも Web Service と同じ処理ができるようになったら、改めて記事を投稿します。

では皆様、良いデータ分析を！