Tuesday, April 28, 2015

NextReports Server: Audits at your hand

By default NextReports Server outputs some events to log files with following action names:
  • Sign in
  • Sign in failed
  • Sign out
  • Add entity
  • Modify entity
  • Delete entity
  • Copy entity
  • Move entity
  • Rename entity
  • Restore entity
  • Grant user / group
  • Revoke user / group
  • Run report
These events have some common properties like id, date, user name, action,session, ip, level (0=info, 1=error), error message. For some events there are also other properties. For example for "Run report" there are report path and duration; for "Grant user" there are entity path, user name to grant for, permissions and recursive flag.

NextReports Server has an extensible auditor feature. A database auditor can be defined to save all these events inside a database. This is very important because NextReports can generate reports over that database to get valuable information.

Inside securityContext.xml there is a bean called auditor:
 <bean class="ro.nextreports.server.audit.CompoundAuditor" id="auditor">
        <property name="auditors">
            <list>
                <ref bean="logAuditor"/>
            </list>
        </property>
</bean>
          

In this list, besides the log auditor we can add a database auditor:

<bean class="ro.nextreports.server.audit.CompoundAuditor" id="auditor">        
        <property name="auditors">             
           <list>
                 <ref bean="logAuditor"/>
                 <ref bean="dbAuditor"/> 
            </list>
        </property>
</bean>       
  
<bean class="ro.nextreports.server.audit.MySqlAuditor" id="dbAuditor">
         <property name="dataSource" ref="auditDataSource"/>
</bean>
   
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="auditDataSource">
      <property name="driverClassName" value="com.mysql.jdbc.Driver"/>      
      <property name="url" value=""/>
      <property name="username" value=""/>
      <property name="password" value=""/>
</bean>

The database behind must contain two tables and a sequence:
  • NS_AUDIT (EVENT_ID, EVENT_DATE, EVENT_USERNAME, EVENT_ACTION, EVENT_SESSION, EVENT_IP, EVENT_LEVEL, EVENT_ERROR_MESSAGE)
  • NS_AUDIT_CONTEXT (EVENT_ID, EVENT_NAME, EVENT_VALUE)
  • NS_AUDIT_SEQ
As stated before, common properties will be saved inside NS_AUDIT, while specific event properties will be saved inside NS_AUDIT_CONTEXT.

For example, for a MySql database we need to call following sqls to initialize:

CREATE TABLE NS_AUDIT (
  EVENT_ID INT UNSIGNED NOT NULL, 
  EVENT_DATE TIMESTAMP, 
  EVENT_USERNAME VARCHAR(50), 
  EVENT_ACTION VARCHAR(30),
  EVENT_SESSION VARCHAR(100), 
  EVENT_IP VARCHAR(20), 
  EVENT_LEVEL  INT(2) , 
  EVENT_ERROR_MESSAGE VARCHAR(150)
) 

CREATE TABLE NS_AUDIT_CONTEXT (
  EVENT_ID INT UNSIGNED, 
  EVENT_NAME VARCHAR(50), 
  EVENT_VALUE VARCHAR(200)
) 
 
CREATE TABLE NS_AUDIT_SEQ( 
  EVENT_ID INT UNSIGNED  NOT NULL,
  CONSTRAINT event_pk PRIMARY KEY (EVENT_ID)
)

INSERT INTO NS_AUDIT_SEQ (EVENT_ID) VALUES(0) 

You should also create an index on EVENT_ID column from NS_AUDIT.

Tuesday, April 14, 2015

NextReports Server: What are the references?

NextReports Server contains (among others) a number of entities like :
  • data sources
  • reports & charts
  • schedulers
  • widgets
  • dashboards
Some of them are referenced by others. For example  any report or chart has a data source. That means a data source is referenced by that report / chart. What happens if we want to delete this data source? User will be informed the action cannot be done because there are other entities that use it.

The message is not very useful to users because if they do not know what are the entities, they must to search for them and delete those first (if the deletion is needed).

From NextReports Server 8.1, users will be informed about these entities with their full path. For example if we want to delete a report which was scheduled by a number of schedulers, the following message will be shown:

In case there are too many referenced entities, only the first 10 will be shown. In next image we want to delete a data source and we will see the list of reports and charts that use it:
In case a report or chart is used (referenced) inside a dashboard as a widget, it can be deleted and the user will see inside dashboard a message that the entity behind it was removed.

Thursday, April 02, 2015

NextReports Server: Dashboard Embedded Code

NextReports Server is able to offer dashboards page as an url like

http://localhost:8081/nextreports-server/app/dashboards

This will bring to users the following page, without header, footer and tabs menu:


From version 8.1 a new Dashboard Embedded Code action is available in dashboard popup actions:


This will give users the url for a specific dashboard:

http://localhost:8081/nextreports-server/app/dashboard?dashboardId=4f6cb7bd-c8df-4ffd-91a9-38c2c2c50b16


This url will show users the dashboard without navigator and buttons and will become the default way of how to show a full dashboard on a monitor screen for visualization.


Thursday, March 19, 2015

NextReports: Iframe new parameters

Starting from NextReports 8.1 iframes will be able to contain in their url some new parameters alongside width, height and report parameters. See this old post about iframe parameters.

Two of them are meaningful for table widgets: tableFontSize and tableCellPadding in points. These can be handy if you want to have an iframe on a big monitor screen and users need to look at it from distance. To show the difference you can compare following two screens:


The second one has tableFontSize=18 and tableCellPadding=10.

Another parameter, adjustableTextFontSize, has a boolean value and by setting it to true the chart from iframe will have texts from title, legends and axis values adjusted in concordance with iframe width. This parameter is automatically set to true on chart Detach action from NextReports Server.

A simple comparison can be seen here:


This example is shown for an iframe with width=800 and height=500 pixels.

Thursday, March 12, 2015

How to access NextReports Server web services in your application

NextReports Server has a number of web services. These can be found in ro.nextreports.server.api. NextReports uses Jersey web services so in any web service class users can see @Path and @QueryParam annotations to know how to create the url. All urls start with:
http://<server_ip>:<server_port>/nextreports-server/api

If users need to access these services using java code, then a client for these services already exists and can be found in ro.nextreports.server.api.client. Inside designer installation folder in lib there is nextreports-server-client-8.0.jar library which can be used. To see how it is used in designer  you can look in ro.nextreports.designer.wizpublish . Here is the code used to publish/download report/charts to/from server. (it involves also how to authenticate, to get list of entities from server, create a folder in server structure and more)

For example to authenticate you have to do the following with the root url previously shown:

WebServiceClient client = new WebServiceClient();   
client.setServer(url);
client.setUsername(userTextField.getText());
client.setPassword(new String(passField.getPassword()));
client.setPasswordEncoder(new Md5PasswordEncoder());

boolean authorized = false;
try {
      authorized = client.isAuthorized();            
} catch (Exception e) {
     // connection error
     e.printStackTrace();
     return false;
}

To get entities from server:

List<EntityMetaData> entities = client.getEntities(folderPath);

This method sets the server url path by appending to base api url "storage/getEntities/" and then the folder path. There are some root constants used to create folderPath like /reports, /charts, /dataSources.

If you want to call Next web services  from Javascript/JQuery and get reports from a path, you have also to be authenticated. Next example uses two javascript libraries:

Base64 encoder (jquery.base64.js) : https://github.com/carlo/jquery-base64/archive/master.zip
MD5 generation (core-min.js and md5-min.js) : https://code.google.com/p/crypto-js/#MD5

This example does an ajax get to obtain the list of reports from root /reports path. Request header contains an Authorization object. On success the list of entities (path and type) is shown in a html list.

<html>
    <head>
       <title>NextReports Server JQuery Test</title>
       <script src="jquery-1.10.2.min.js" type="text/javascript"></script>
       <script src="jquery.base64.js" type="text/javascript"></script>
       <script src="core-min.js" type="text/javascript"></script>
       <script src="md5-min.js" type="text/javascript"></script>
       <script>       
             var serverPath = "/reports";    
             var username="admin";
             var password="1";       
             var passwordHash = CryptoJS.MD5("1");
             var base64 = $.base64.encode( username + ":" + passwordHash );                
             $.ajax( {               
                    type: "GET",
                    url : 'http://localhost:8081/nextreports-server/api/storage/getEntities?path=' + serverPath,
                    dataType : "xml",               
                    beforeSend : function(xhr) {            
                            console.log(base64);
                            xhr.setRequestHeader('Authorization', 'Basic ' + base64);
                    },        
                   error : function(xhr, ajaxOptions, thrownError) {
                            console.log("error");            
                            alert(thrownError);
                   },
                   success : function(data) {            
                            console.log("success");            
                            console.log(data);        
                            $('#title').append(serverPath);     
                            var paths = data.getElementsByTagName("path");
                            var types = data.getElementsByTagName("type");
                            var root = $("#list");
                            for (var i=0; i<paths.length; i++) {
                                  var type = "";
                                  if (types[i].textContent === "1") {
                                      type = "folder";
                                  } else if (types[i].textContent === "3") {
                                      type = "next-report";
                                  } else if (types[i].textContent === "4") {
                                      type = "jasper-report";
                                  }                  
                                  root.append('<li>'+paths[i].textContent+ ' (' + type + ')</li>');
                           }
                   }
         });
    </script>
    </head>    
    <body>
        <div>
            <span id="title">List of reports from path: </span>
            <ul id="list"></ul>
        </div>
    </body>

To be able to run this example you must be in the same domain as server, otherwise a cross domain exception will be seen in console. From NextReports Server 8.1 inside web.xml for jersey.springServlet you can uncomment the following to allow for cross domain testing calls (please be sure to comment it back after you make your tests):

       <init-param>
            <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
            <param-value>ro.nextreports.server.web.core.ResponseCorsFilter</param-value>
        </init-param>