Eclipse BIRT dengan RESTful API

Pada artikel ini saya ingin membahas tentang bagaimana BIRT (Business Intelligence Reporting Tools) sebuah platform open source untuk reporting dari Eclipse dapat memanfaatkan JSON dari REST API sebagai sumber data. Untuk lebih mengenal BIRT dapat mengunjungi laman ini dan ini. Sebenarnya kenapa memilih BIRT adalah suatu pertimbangan yang relatif dan berikut adalah alasan tersebut :

  1. BIRT merupakan platform yang dapat berdiri sendiri alias stand-alone.
  2. Memiliki Viewer yang web-native dengan teknologi Java dan dapat membuka file report tanpa perlu compile seperti Jasper.
  3. Web viewer ini dapat menjadi solusi untuk memiliki feature atau komponen yang loose coupling sehingga setidaknya memiliki rasa “Microservice”
  4. Secara kode cukup tidak terlalu banyak menulis koding di sisi backend dan tidak dari nol karena dapat menggunakan berbagai macam Data Sources yang sudah tersedia secara out of the box.
  5. The most thing that I love about the report design is XML based.

Karena saat ini banyak aplikasi yang sedang hype untuk penggunaan REST API, muncul rasa penasaran

Apakah BIRT dapat menggunakan JSON dari REST API sebagai data source seperti yang saya sampaikan di awal artikel?

Secara eksplisit pada website Eclipse BIRT mungkin tidak dijelaskan mendetail tentang kapabilitas ini, namun ternyata BIRT memiliki kemampuan untuk mengakomodirnya yaitu dengan komponen Scripted Data Source.


Development

Untuk studi kasusnya akan menghasilkan report yang menampilkan data dari sebuah REST API dengan URL (akses menggunakan metode POST dengan security token tentunya) :

POST http://localhost:1081/api/sample/v1/get-revenue

Data yang diperoleh dari REST URL tersebut berbentuk JSON seperti di bawah ini :

{
data : [
{id : 1, customer: 'PT Brantas', revenue : '18500000000.00'},
{id : 2, customer: 'PT Ardhaya Bima', revenue : '22305000000.00'},
{id : 3, customer: 'CV Satelit Inovasi', revenue : '1530000105.00'},
{id : 4, customer: 'PT Indo Persada', revenue : '71200002400.00'},
{id : 5, customer: 'PT Malika Sari', revenue : '25100300.00'},
{id : 6, customer: 'CV Rempoa Kholis', revenue : '1400502.00'}]
}

Untuk memulainya kita dapat mengunduh designer Eclipse BIRT dengan versi terbaru, lalu silakan membuat project report dan sebuah blank report. Mari ikuti langkah dan penjelasannya sebagai berikut :

  • Buka Report Design Perspective.
  • Demi keamanan API yang akan diakses tentu membutuhkan token autentikasi. Pada Bagian Report Parameters klik kanan pilih New Parameter dan set dengan tipe data String beri nama token. Value parameter ini nanti akan diberikan melalui query string.
Parameter Security Token
  • Pada bagian Data Explorer pilih Data Sources klik kanan pilih New Data Source.
  • Pilih Scripted Data Source lalu silakan diberi nama serta tekan tombol Finish.
Scripted Data Source
  • Sebelum masuk ke tahap selanjutnya, apa yang sebetulnya dimaksud dengan Data Sources? Dan apa bedanya dengan Data Sets? Data Sources adalah sumber data dari sebuah report, dapat berupa JDBC Data Source, MongoDB, POJO, Web Services, dan lain sebagainya. Sedangkan Data Sets adalah tabel-tabel atau entitas object yang ada di setiap Data Sources. Kita pun dapat melakukan join antar Data Sets yang berbeda data Sources.
  • Pada bagian Data Explorer pilih Data Sets klik kanan pilih New Data Set. Pastikan value dari field Data Set Type adalah Scripted Data Set.
  • Silakan diberi nama serta tekan tombol Finish.
Scripted Data Set
  • Selanjutnya kita membutuhkan variable dari entitas JSON yang akan diterima sebagai kolom. Buka dialog dari Data Set yang baru saja dibuat pada bagian Output Columns tambahkan seperti berikut.
Kolom Sesuai Field JSON
  • Sampai dengan tahap ini, kita sudah selesai untuk menyiapkan object yang akan digunakan untuk scripting. Selanjutnya silakan pilih Data Set yang baru saja dibuat. Pada bagian editor pilih tab Script maka akan ada beberapa opsi tahapan siklus Data Set yang dapat kita manipulasi dengan scripting seperti open, describe, fetch, close, dan lain sebagainya.
  • Oiya bahasa scripting yang kita ketik adalah Rhino, sebuah engine Javascript yang dibangun menggunakan Java. Jadi bagi yang sudah terbiasa dengan Javascript seharusnya tidak terlalu asing. Untuk mengeksekusi URL REST API maka silakan pilih opsi open dan silakan tambahkan baris kode sebagai berikut.
logger = java.util.logging.Logger.getLogger("birt.report.logger");
importPackage(Packages.java.io);
importPackage(Packages.java.net);
//ini perintah untuk logging ya, karena kebetulan tidak ada fitur debug
logger.info('Selesai import package…');
var HTTP_POST = "POST";
var HTTP_GET = "GET";
var SAMPLE_API = "http://localhost:1081";
var BEARER = "Bearer ";
var AUTHORIZATION = "Authorization";
var TOKEN = "token";
var HTTP_CONTENT_TYPE = "Content-Type";
var APPLICATION_JSON = "application/json";
var UTF_8 = "UTF-8";
//ini perintah untuk get parameter token melalui URL, ?token=xxxxx
var pToken = params['token'];
logger.info('pToken = ' + pToken);
//base function untuk mendapatkan response dari URL REST API
function subscribe(url, method, token) {
var address = new URL(url);
var urlConnection = address.openConnection();
urlConnection.setDoOutput(true);
urlConnection.setDoInput(true);
urlConnection.setRequestMethod(method);
urlConnection.setRequestProperty(HTTP_CONTENT_TYPE, APPLICATION_JSON+"; "+UTF_8);
if(token != null) {
var bearerToken = BEARER + token;
logger.info(">> bearerToken = " + bearerToken);
urlConnection.setRequestProperty(AUTHORIZATION, bearerToken);
}
urlConnection.connect();
var buffReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
var inputLine;
var response = "";
while ((line = buffReader.readLine()) != null) {
response += line;
}
buffReader.close();
urlConnection.disconnect();
return response;
}
//main function untuk memanggil API dalam bentuk array yang berasal dari JSON
function getData(){
var token = pToken;
var revenueApi = SAMPLE_API + "/api/sample/v1/get-revenue";
var response = subscribe(revenueApi, HTTP_POST, token);
logger.info('>> response = ' + response);
var json = JSON.parse(response);
return json['data'];
}
rowNo = 0;
data = getData();
  • Selanjutnya adalah untuk mapping data dari JSON yang diterima dari opsi open ke bagian Output Columns, silakan pilih opsi fetch dan tambahkan baris kode sebagai berikut.
length = data.length;
if (rowNo >= length){
return false;
}
else{
row["id"] = data[rowNo].id;
row["customer"] = data[rowNo].customer;
row["revenue"] = data[rowNo].revenue;
rowNo++;
return true;
}
  • Semisal kita memiliki security token sebagai berikut, kita dapat menginputkan value dari access_token tersebut sebagai default value pada parameter token.
Token dari autentikasi Sign-In API
  • Selanjutnya kita dapat mencoba melihat hasil dari script kita atau Preview Results, jika benar maka akan menghasilkan result sebagai berikut dan muncul print log pada terminal Eclipse (jika kita running Eclipse melalui command prompt di Windows atau terminal di UNIX).
Preview Result dari API get-revenue pada Data Sets BIRT
Eksekusi Logger pada Terminal Sesuai Script
  • Jika sudah tampil, maka kita dapat melanjutkan ke bagian Report Designer untuk menghasilkan laporan dalam bentuk web yang nanti dapat dieksport ke dalam beberapa format sesuai fitur BIRT. Untuk design report kita dapat membuatnya dalam bentuk seperti di bawah ini (cukup dengan drag and drop Data Sets yang telah dibuat jika ingin generate table, ringkas bukan?)
Desain Tampilan BIRT

Hasil Akhir

Setelah selesai dengan design tampilan, tahap terakhir adalah untuk preview hasilnya pada browser dengan menginputkan token security. Silakan tekan tombol “View Report in Web Viewer”.

Input Security Token

By the way security token tersebut nantinya dapat diotomatisasi input valuenya melalui query parameter URL seperti berikut :

http://birt-viewer/nama_report.rptdesign?token=[security_token_anda]
Hasil Eksekusi Generate Report pada Browser

Untuk template report beserta source code secara keseluruhan ada pada GIST di bawah ini.

<?xml version="1.0" encoding="UTF-8"?>
<report xmlns="http://www.eclipse.org/birt/2005/design" version="3.2.23" id="1">
<property name="createdBy">Eclipse BIRT Designer Version 4.6.0.v201606072122</property>
<property name="units">in</property>
<property name="iconFile">/templates/blank_report.gif</property>
<property name="bidiLayoutOrientation">ltr</property>
<property name="imageDPI">72</property>
<parameters>
<scalar-parameter name="token" id="6">
<text-property name="helpText">Application Token</text-property>
<property name="valueType">static</property>
<property name="dataType">string</property>
<property name="distinct">true</property>
<simple-property-list name="defaultValue">
<value type="constant">eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJBZG1pbmlzdHJhdG9yIiwiaWF0IjoxNTgwNjcxOTg5LCJleHAiOjE1ODA2NzkxODl9.IrF7mxccNOTd5XROZeYXBa1V8_rX67NKmdoVnR6kfLFDae7g0q-Di6PrStJs81SmVbMXIw23j0mr8pP00EqvzA</value>
</simple-property-list>
<list-property name="selectionList"/>
<property name="paramType">simple</property>
<property name="controlType">text-box</property>
<structure name="format">
<property name="category">Unformatted</property>
</structure>
</scalar-parameter>
</parameters>
<data-sources>
<script-data-source name="restDataSource" id="4"/>
</data-sources>
<data-sets>
<script-data-set name="restDataSet" id="5">
<list-property name="resultSetHints">
<structure>
<property name="position">1</property>
<property name="name">id</property>
<property name="dataType">integer</property>
</structure>
<structure>
<property name="position">2</property>
<property name="name">customer</property>
<property name="dataType">string</property>
</structure>
<structure>
<property name="position">3</property>
<property name="name">revenue</property>
<property name="dataType">decimal</property>
</structure>
</list-property>
<list-property name="columnHints">
<structure>
<property name="columnName">id</property>
</structure>
<structure>
<property name="columnName">customer</property>
</structure>
<structure>
<property name="columnName">revenue</property>
</structure>
</list-property>
<structure name="cachedMetaData">
<list-property name="resultSet">
<structure>
<property name="position">1</property>
<property name="name">id</property>
<property name="dataType">integer</property>
</structure>
<structure>
<property name="position">2</property>
<property name="name">customer</property>
<property name="dataType">string</property>
</structure>
<structure>
<property name="position">3</property>
<property name="name">revenue</property>
<property name="dataType">decimal</property>
</structure>
</list-property>
</structure>
<property name="dataSource">restDataSource</property>
<method name="open"><![CDATA[logger = java.util.logging.Logger.getLogger("birt.report.logger");
importPackage(Packages.java.io);
importPackage(Packages.java.net);
//ini perintah untuk logging ya, karena kebetulan tidak ada fitur debug
logger.info('Selesai import package…');
var HTTP_POST = "POST";
var HTTP_GET = "GET";
var SAMPLE_API = "http://localhost:1081&quot;;
var BEARER = "Bearer ";
var AUTHORIZATION = "Authorization";
var TOKEN = "token";
var HTTP_CONTENT_TYPE = "Content-Type";
var APPLICATION_JSON = "application/json";
var UTF_8 = "UTF-8";
//ini perintah untuk get parameter token melalui URL, ?token=xxxxx
var pToken = params['token'];
logger.info('pToken = ' + pToken);
//base function untuk mendapatkan response dari URL REST API
function subscribe(url, method, token) {
var address = new URL(url);
var urlConnection = address.openConnection();
urlConnection.setDoOutput(true);
urlConnection.setDoInput(true);
urlConnection.setRequestMethod(method);
urlConnection.setRequestProperty(HTTP_CONTENT_TYPE, APPLICATION_JSON+"; "+UTF_8);
if(token != null) {
var bearerToken = BEARER + token;
logger.info(">> bearerToken = " + bearerToken);
urlConnection.setRequestProperty(AUTHORIZATION, bearerToken);
}
urlConnection.connect();
var buffReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
var inputLine;
var response = "";
while ((line = buffReader.readLine()) != null) {
response += line;
}
buffReader.close();
urlConnection.disconnect();
return response;
}
//main function untuk memanggil API dalam bentuk array yang berasal dari JSON
function getData(){
var token = pToken;
var revenueApi = SAMPLE_API + "/api/sample/v1/get-revenue";
var response = subscribe(revenueApi, HTTP_POST, token);
logger.info('>> response = ' + response);
var json = JSON.parse(response);
return json['data'];
}
rowNo = 0;
data = getData();]]></method>
<method name="fetch"><![CDATA[length = data.length;
if (rowNo >= length){
return false;
}
else{
row["id"] = data[rowNo].id;
row["customer"] = data[rowNo].customer;
row["revenue"] = data[rowNo].revenue;
rowNo++;
return true;
}]]></method>
</script-data-set>
</data-sets>
<page-setup>
<simple-master-page name="Simple MasterPage" id="2">
<page-footer>
<text id="3">
<property name="contentType">html</property>
<text-property name="content"><![CDATA[<value-of>new Date()</value-of>]]></text-property>
</text>
</page-footer>
</simple-master-page>
</page-setup>
<body>
<table id="1531">
<property name="borderBottomStyle">solid</property>
<property name="borderBottomWidth">thin</property>
<property name="borderLeftStyle">solid</property>
<property name="borderLeftWidth">thin</property>
<property name="borderRightStyle">solid</property>
<property name="borderRightWidth">thin</property>
<property name="borderTopStyle">solid</property>
<property name="borderTopWidth">thin</property>
<property name="dataSet">restDataSet</property>
<list-property name="boundDataColumns">
<structure>
<property name="name">id</property>
<text-property name="displayName">id</text-property>
<expression name="expression" type="javascript">dataSetRow["id"]</expression>
<property name="dataType">integer</property>
</structure>
<structure>
<property name="name">customer</property>
<text-property name="displayName">customer</text-property>
<expression name="expression" type="javascript">dataSetRow["customer"]</expression>
<property name="dataType">string</property>
</structure>
<structure>
<property name="name">revenue</property>
<text-property name="displayName">revenue</text-property>
<expression name="expression" type="javascript">dataSetRow["revenue"]</expression>
<property name="dataType">decimal</property>
</structure>
</list-property>
<column id="1550"/>
<column id="1551"/>
<column id="1552"/>
<header>
<row id="1532">
<cell id="1533">
<label id="1534">
<text-property name="text">ID</text-property>
</label>
</cell>
<cell id="1535">
<label id="1536">
<text-property name="text">Customer</text-property>
</label>
</cell>
<cell id="1537">
<label id="1538">
<text-property name="text">Revenue</text-property>
</label>
</cell>
</row>
</header>
<detail>
<row id="1539">
<property name="borderBottomStyle">solid</property>
<property name="borderBottomWidth">thin</property>
<property name="borderTopStyle">solid</property>
<property name="borderTopWidth">thin</property>
<cell id="1540">
<property name="borderRightStyle">solid</property>
<property name="borderRightWidth">thin</property>
<data id="1541">
<property name="fontSize">8pt</property>
<list-property name="boundDataColumns">
<structure>
<property name="name">id</property>
<text-property name="displayName">id</text-property>
<expression name="expression" type="javascript">dataSetRow["id"]</expression>
<property name="dataType">integer</property>
<property name="allowExport">true</property>
</structure>
</list-property>
<property name="resultSetColumn">id</property>
</data>
</cell>
<cell id="1542">
<property name="borderRightStyle">solid</property>
<property name="borderRightWidth">thin</property>
<data id="1543">
<property name="fontSize">8pt</property>
<list-property name="boundDataColumns">
<structure>
<property name="name">customer</property>
<text-property name="displayName">customer</text-property>
<expression name="expression" type="javascript">dataSetRow["customer"]</expression>
<property name="dataType">string</property>
<property name="allowExport">true</property>
</structure>
</list-property>
<property name="resultSetColumn">customer</property>
</data>
</cell>
<cell id="1544">
<data id="1545">
<property name="fontSize">8pt</property>
<list-property name="boundDataColumns">
<structure>
<property name="name">revenue</property>
<text-property name="displayName">revenue</text-property>
<expression name="expression" type="javascript">dataSetRow["revenue"]</expression>
<property name="dataType">decimal</property>
<property name="allowExport">true</property>
</structure>
</list-property>
<property name="resultSetColumn">revenue</property>
</data>
</cell>
</row>
</detail>
</table>
</body>
</report>

Jika seandainya menginginkan improvisasi seperti format money atau fungsi-fungsi lainnya, kita dapat memanfaatkan built-in function dari BIRT tanpa perlu merubah koding dari backend atau sisi API. Cukup lengkap, praktis, dan tinggal pakai.

Additional Function BIRT

Oke sekian dari saya jika ada komentar atau diskusi yuk mari kita berbagi ilmu. Jika ada yang kurang jelas maaf ya karena maklum ini adalah artikel pertama saya hehe. Terimakasih 😀

Author: nandra

Source code, photograph, and notes. Instagram 📸 : nandcep, Steller 🏕 : nancep, and Github 💻 : nandcep

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s