Etiqueta: birt

angular – Spring GetMapping with JSON parameter throwing HTTP 400 – Bad Request

I had a pretty simple REST service implemented with Spring Web and it was throwing a HTTP 400 – Bad Request Error.

This was the implementation

@GetMapping("titulos/preview")
    public void preview(@RequestParam String titulo, HttpServletResponse response) throws DatatypeConfigurationException, JAXBException, IOException {
        log.debug("REST request to preview Titulo: {}", titulo);
        Titulo tituloParsed = mapper.readValue(titulo, Titulo.class);
        String xml = titulosService.toXML(tituloParsed);
        String url = "http://birtviewer/viewer/run?__report=report/rptTitulo.rptdesign&__format=pdf";
        url += "&payload=" + URLEncoder.encode(xml, "utf-8");
        response.sendRedirect(url);
    }

Nothing wrong with it, it just receives JSON as a parameter and converts it to its XML representation in order to forward it to a BIRT report viewer instance. Still, it was throwing the aforementioned error whenever I was calling it from the client with a JSON payload, opening in a new window or tab.

window.open(`${this.resourceUrl}/preview?titulo=${JSON.stringify(titulo)}`, '_blank');

It turned out that I wasn’t URL encoding the JSON value before sending it to the service. This was causing the error due to the parameter not being recognized and being required by default. I just had to do that to get rid of the error.

window.open(`${this.resourceUrl}/preview?titulo=${encodeURIComponent(JSON.stringify(titulo))}`, '_blank');

sources:
https://stackoverflow.com/questions/29823626/angular-http-post-to-target-blank
https://stackoverflow.com/questions/38372134/how-to-convert-an-object-to-json-correctly-in-angular-2-with-typescript
https://stackoverflow.com/questions/40799609/set-headers-while-submitting-a-form-data
https://developer.mozilla.org/es/docs/Web/HTTP/Status/400

Anuncios

birt – add report border pattern

Screenshot from 2019-06-04 15-05-42

I was trying to add a border with a pattern in a report. As it happens, the easiest way to do this is to simply use a background image as recalled here.

First, let’s create our background image with the help of GIMP. Create a new image using the template US-Letter (300 ppp) and adjust the Advanced Settings to set a resolution of 72 ppp.

Screenshot from 2019-06-04 15-11-00

then add the pattern you would like to use. I just pasted an image and scaled it to fit the size of the page. You can use advanced image search from google to specify the size you are looking for 612×792 pixels in this case. You can optimize your image by removing the background and maybe specify a color palette or convert it to black and white and use GIF format, I didn’t need it though. Export the resulting image and copy it to your report project folder.

Screenshot from 2019-06-04 15-12-45

Screenshot from 2019-06-04 15-13-31

Finally, set this image as Background image in the Master Page of your report in the Advanced tab of the Property Editor. You may also want to set the Background size width to cover for a better fit.

Screenshot from 2019-06-04 15-16-04

Depending on the output the results would vary. In our case we only needed it in PDF format which dispplays just fine.

birt – create report from XML data sent as parameter

Not long ago I published an article on how to create a report from JSON sent as parameter. Now is the turn for XML data. The principles are almost the same.

We need to create a new XML data source pointing to a XML file which contains the actual data to be displayed on the report or the XSD definition file.

Screenshot from 2019-06-04 14-32-12

Screenshot from 2019-06-04 14-33-39

then we can define our Data Set using this Data Source

Screenshot from 2019-06-04 14-38-48

Screenshot from 2019-06-04 14-39-09

We need to select which node contains the records we’re going to show and map the fields accordingly.

Screenshot from 2019-06-04 14-39-46

Screenshot from 2019-06-04 14-39-58

Screenshot from 2019-06-04 14-40-44

Screenshot from 2019-06-04 14-40-53

Screenshot from 2019-06-04 14-41-06

Now we can drag and drop our Data Set to create a table in our report.

Screenshot from 2019-06-04 14-42-26

Screenshot from 2019-06-04 14-43-52

PDF
Screenshot from 2019-06-04 14-44-12

Let’s create our parameter named ‘payload’ which will receive the XML sent from the client.

Screenshot from 2019-06-04 14-45-11

now, let’s replace at runtime the XML within the file with the XML we received as parameter. Just add the following code to the script tab of the Data Source we created earlier.

importPackage( Packages.java.io );

xml = new java.lang.String(params['payload']);
bais = new ByteArrayInputStream(xml.getBytes());
ctx = reportContext.getAppContext();

ctx.put('org.eclipse.datatools.enablement.oda.xml.inputStream', bais);

boom! we’re done.

sources:
https://cirovladimir.wordpress.com/2019/05/15/birt-create-report-from-json-data-sent-as-parameter/

Use JSON as a Scripted Data Set


https://www.eclipse.org/forums/index.php/t/162952/

birt – create report from JSON data sent as parameter

Normally you would construct your reports from data that is already stored somewhere, it could be a database, a file or something else. I was required to build a preview of a report -that is, before the data were inserted- so that the user can confirm the information before making the request.

Since the data is sent in JSON format already, I thought I could elaborate a report from this same data.

{
    "averiguacion":null, 
    "actores":[
        {
            "genero":"F", 
            "fechaNacimiento":"1980-05-13", 
            "origenIngreso":"FORMAL", 
            "idMunicipio":"22014", 
            "idEstado":"22", 
            "idColonia":"22014010800001", 
            "nombre":"ROSA", 
            "itemId":1, 
            "apellidos":"MENDEZ", 
            "idLugarNacimiento":3, 
            "ingresoMensual":2500, 
            "calle":"SIN REGISTRO", 
            "numero":0, 
            "codigoPostal":0, 
            "direccion":"LINDAVISTA", 
            "principal":"X"
        }
    ], 
    "demandados":[
        {
            "genero":"M", 
            "fechaNacimiento":"2019-05-13", 
            "origenIngreso":"FORMAL", 
            "idMunicipio":"22014", 
            "idEstado":"22", 
            "idColonia":"22014010800001", 
            "nombre":"JORGE", 
            "itemId":1, 
            "apellidos":"HERNANDEZ", 
            "calle":"SIN REGISTRO", 
            "numero":0, 
            "codigoPostal":0, 
            "direccion":"LOMA BONITA", 
            "principal":"X"
        }
    ], 
    "terceros":[
    ], 
    "observaciones":"LA SOLICITANTE VIENE GOLPEADA", 
    "compareciente":"VICTIMA", 
    "anexos":[
        {
            "__module":{
            }, 
            "tipo":"OFRECIDO", 
            "descripcion":"IDENTIFICACION OFICIAL"
        }, 
        {
            "__module":{
            }, 
            "tipo":"OFRECIDO", 
            "descripcion":"ACTA DE MATRIMONIO"
        }
    ], 
    "medidasSolicitadas":[
        {
            "nombre":"ARRAIGO", 
            "id":17, 
            "_selection_34":true
        }, 
        {
            "nombre":"CUSTODIA DE INFANTES", 
            "id":8, 
            "_selection_34":true
        }
    ], 
    "domicilioDesahogo":"LINDAVISTA", 
    "horarioDomicilioDemandado":"LUNES A VIERNES DESPUES DE LAS 16HRS"
}

We are going to need some variables to store counts and serialize the JSON into an object, let’s select the Outline tab and click on the report name, then in the script tab let’s define the following variables -which will be accessible from the fetch events we’ll see later-

Screenshot from 2019-05-15 12-43-23

var jsonPayload;
var countActores = 0;
var countDemandado = 0;
var countMedidas = 0;
var countAnexos = 0;

Create a new scripted data source, let’s call it ‘jsonPayload’. Then add a new report parameter called ‘payload’ of type String and optionally establish a default value, i.e. {"actores":[{"nombre":"ROSA"}]}. Select the jsonPayload data source and in the Script tab in the ‘open’ event we’ll serialize the JSON data into the jsonPayload variable we define earlier

jsonPayload = eval('(' + params['payload'] + ')');

With this, you could insert a DynamicText anywhere in your report to access the properties in jsonPayload object. Just enter the expression in the Expression builder like so

Screenshot from 2019-05-15 12-55-55

jsonPayload.observaciones

Now, let’s create a new data set, using the jsonPayload scripted datasource and define the columns corresponding to the JSON data, for each array list property that we have in our JSON, i.e. actores

Screenshot from 2019-05-15 13-10-43

Let’s call it jsonActores and in the fetch event in the script tab for this dataset, let’s read the values from our jsonPayload object. This method returns true if there’s more data to load and false otherwise

if(countActores < jsonPayload.actores.length){
    row['nombre'] = jsonPayload.actores[countActores].nombre;
    row['apellidos'] = jsonPayload.actores[countActores].apellidos;
    row['fechaNacimiento'] = jsonPayload.actores[countActores].fechaNacimiento;
    row['genero'] = jsonPayload.actores[countActores].genero;
    row['lugarNacimiento'] = jsonPayload.actores[countActores].lugarNacimiento;
    row['origenIngreso'] = jsonPayload.actores[countActores].origenIngreso;
    row['ingresoMensual'] = jsonPayload.actores[countActores].ingresoMensual;
    countActores++;
    return true;
} else {
    return false;
}

that’s it, now we can drag and drop our dataset in the report as any other.

You can pass the parameter, to an instance of the BIRT report viewer for example, in the URL like so

http://127.0.0.1:57045/viewer/run?__report=Preview.rptdesign&__format=pdf&payload={…json data…}

sources:
https://stackoverflow.com/questions/30349864/use-json-as-a-scripted-data-set-in-birt

Use JSON as a Scripted Data Set

birt report drill down url window location base path problem

You can navigate to the details of a certain data category in a BIRT report using the hyperlink property of an element and use the drill down option to configure it.

Selection_007

Hyperlink Options _006

the problem for us was that when deploying the report to an instance of the ReportViewer it added some wrong path prefix to the report path variable ‘__report’.

So we had to construct the path ourselves. First change the drill-trough option to URI, and the Location to javascript syntax.

We tried to access the window object from a script in the report (since the scripts are javascript) and use it to find out the base url and path, but it failed complaining that ‘window’ was not defined. I suppose you can’t access the window object, I couldn’t find one.

importPackage(Packages.java.text);
sdf = new SimpleDateFormat('yyyy-MM-dd');

window.location.origin + window.location.pathname +
'?__format=pdf&__report=report/tsj/DetallesIndicadores/DetalleCarpetasIniciadas.rptdesign' +
'&desde=' + sdf.format(params['desde']) +
'&hasta=' + sdf.format(params['hasta'])

But it turns out, as I discovered through this forum post, you can use the report context object to access the HttpServletRequest which we can use to construct our URL.

Then you just have to format and add your parameters to the URL.

Ours ended up like this

importPackage(Packages.java.text);
sdf = new SimpleDateFormat('yyyy-MM-dd');

reportContext.getHttpServletRequest().getRequestURL().toString() +
'?__format=pdf&__report=report/tsj/DetallesIndicadores/DetalleCarpetasIniciadas.rptdesign' +
'&desde=' + sdf.format(params['desde']) +
'&hasta=' + sdf.format(params['hasta'])

remember the last line is returned as the value for our URL, in this case the concatenation of our values.

sources:
https://www.eclipse.org/forums/index.php/m/1235339/?srch=window.location#msg_1235339
https://help.eclipse.org/mars/index.jsp?topic=%2Forg.eclipse.birt.doc.isv%2Fenginescript%2Fapi%2Forg%2Feclipse%2Fbirt%2Freport%2Fengine%2Fapi%2Fscript%2FIReportContext.html
https://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/HttpServletRequest.html
https://stackoverflow.com/questions/1629102/root-url-of-the-servlet

Eclipse – Consulta dinámica en BIRT

Para modificar la consulta de un DataSet puedes utilizar la sección “Property Binding”

en mi caso necesitaba reemplazar en la conulta el valor de un parámetro.

Para reemplazar una cadena con otra lo puedes hacer en el “Expression Builder” de la siguiente manera



var q=new Packages.java.lang.String("SELECT COUNT(*) AS TOTAL \
FROM @OFICINA.EMPLEADOS A, @OFICINA.PUESTOS R, \
@OFICINA.SALARIOS D, @OFICINA.DEPTOS O \
WHERE A.NID_ACO_EXP= R.NID_ACO \
R.NID_DMD=D.NID_DMD AND R.NORDEN=O.NID_ORDEN \
AND TRUNC(R.FECHA) BETWEEN :INICIO AND :FIN");
q.replaceAll("@OFICINA",params["OFICINA"].value);

el código es javascript, pero podemos utilizar clases Java también. En este caso utilizamos la clase String de Java en vez de Javascript, porque el método de javascript no reemplaza todas las coincidencias.

Fuentes:
birt exchange

Eclipse – Titulo dinámico en el encabezado (master page)

Cuando tienes varios reportes, es buena idea crear una librería para reutilizar elementos en los reportes. Un ejemplo común es cuando necesitamos usar el mismo encabezado y pie de página para los reportes.

BIRT nos permite agregar algunos elementos dinámicos -tales como el número de páginas, la fecha e incluso un valor obtenido mediante código javascript en un DynamicText- El problema es que esto dependerá de cada reporte, así que ¿cómo agregamos elementos dinámicos desde una librería a nuestro reporte?

Pues habrá que crear una “master page” en la librería donde pongamos elementos “DynamicText” en el encabezado y que estos desplieguen el valor de un parámetro. Después podemos modificar el valor del parámetro en cada reporte, y así, tener un título personalizado pero manteniendo el formato.

Si además, queremos obtener el título mediante una consulta a la base de datos. Lo que tenemos que hacer es crear el DataSource a la base de datos y un DataSet con la consulta que obtiene el título, por ejemplo


SELECT DESCRIPCION
FROM OFICINAS
WHERE ID=:OFICINA

Luego, debemos poner el siguiente código en el evento “beforeFactory” de nuestro reporte


//public final static int MODE_GENERATION = 1; //This mode is for generate the report document. Typically used in Report Engine run task
//public final static int MODE_PRESENTATION = 2; //This mode is for present data that saved in report document, without further data manipulation operation. Typically used in report engine render task
//public final static int DIRECT_PRESENTATION = 3;//This mode is to execute the data query without create report document. It is used in report engine runAndRender task.
//public final static int MODE_UPDATE = 4; //This mode is used to update the existing report document with new queries. Typicially used in IV.
importPackage( Packages.org.eclipse.birt.report.model.api );
importPackage(Packages.java.lang);
importPackage(Packages.java.util);
importPackage(Packages.org.eclipse.birt.report.data.adapter.api);
importPackage(Packages.org.eclipse.birt.report.model.api);
importPackage(Packages.org.eclipse.birt.data.engine.api.querydefn);
var myconfig = reportContext.getReportRunnable().getReportEngine().getConfig();
var des = DataRequestSession.newSession(myconfig, new DataSessionContext(3));
var dsrc = reportContext.getDesignHandle().findDataSource("dsrcOficina");
var dset = reportContext.getDesignHandle().findDataSet("dsetOficina");
des.defineDataSource(des.getModelAdaptor().adaptDataSource(dsrc));
des.defineDataSet(des.getModelAdaptor().adaptDataSet(dset));
var paramBinding = new InputParameterBinding("param_1", new ScriptExpression("\""+ params['IDOFICINA'].value +"\"") );
queryDefinition = new QueryDefinition( );
queryDefinition.setDataSetName( "dsetOficina" );
queryDefinition.addInputParamBinding( paramBinding );
queryDefinition.setAutoBinding(true);
var pq = des.prepare( queryDefinition );
var qr = pq.execute( null );
var ri = qr.getResultIterator( );
if ( ri.next( ) )
{
params["Titulo"].value = ri.getString("DESCRIPCION");
}
ri.close( );
qr.close( );
des.shutdown( );

Es importante poner los nombres exactamente igual que en el diseñador.
En nuestro caso, el parámetro “IDOFICINA” es de tipo String, así que lo ingresamos de la siguiente manera


var paramBinding = new InputParameterBinding( "param_1",new ScriptExpression("\""+ params['IDOFICINA'].value +"\"") );

si fuera un Integer tendría que ser de la siguiente manera


var paramBinding = new InputParameterBinding( "param_1",new ScriptExpression(params['IDOFICINA'].value) );

El valor de ScriptExpression debe ser un bloque de javascript válido, cómo si lo ingresaramos en la ventana “ExpressionBuilder” 🙂

Fuentes:
eclipse forums
birt exchange