Run custom javascript to modify report inputs/outputs or to send a report
Define global functions beforeRender
or (and) afterRender
in script and use parameters req
and res
to reach your needs. script functions expect parameters to be req, res, done
or req, res
.
async function beforeRender(req, res) {
// merge in some values for later use in engine
// and preserve other values which are already in
req.data = Object.assign({}, req.data, {foo: "foo"})
req.data.computedValue = await someAsyncComputation()
}
or
function beforeRender(req, res, done) {
// merge in some values for later use in engine
// and preserve other values which are already in
req.data = Object.assign({}, req.data, {foo: "foo"})
done()
}
You can require
external modules in the node.js way, however you need to first enable them in the config.
{
"extensions": {
"scripts": {
"allowedModules": ["axios"]
}
}
}
Alternatively you can enable all modules using extensions.scripts.allowedModules="*"
or using config allowLocalFilesAccess=true
The following example uses popular axios module to make a rest call and nodemailer to email the output service.
//load some data from the remote API
const axios = require('axios')
async function beforeRender(req, res) {
const r = await axios.get('http://localhost:5488/odata/templates')
req.data = { ...req.data, ...r.data}
}
//send the pdf report by mail
const nodemailer = require('nodemailer')
async function afterRender(req, res) {
const smtpTransport = nodemailer.createTransport({...})
const mail = {
from: '..>',
to: '...',
subject: '...',
text: '...',
attachments: [
{
filename: 'Report.pdf',
content: new Buffer(res.content)
}],
}
try {
await smtpTransport.sendMail(mail)
} finally {
smtpTransport.close()
}
}
req.template
- modify report template, mostly content
and helpers
attributesreq.data
- json object used to modify report input datareq.context.http
- object which contain information about input http headers, query params, etc. (this is opt-in and you need to enable it using config extensions.express.exposeHttpHeaders=true
)res.content
- buffer with output reportres.meta.headers
- output headersYou can associate multiple scripts to the report template. The scripts are then serially executed one after one in the order specified in the jsreport studio. The req
and res
parameters are properly passed through the scripts chain. This means you can use for example req.data
to pass information between scripts.
You can set up a script to run for every single template by adding flag isGlobal
. This can be done for example in jsreport studio script's properties. Global script can be useful for common tasks like adding common helpers or settings to templates.
You can reach environment variables using node.js process module.
const process = require('process')
function beforeRender(req, res) {
const myEnv = process.env.MY_ENV
}
You can also read your config file if you prefer it.
const path = require('path')
const promisify= require('util').promisify
const readFileAsync = promisify(require('fs').readFile)
async function beforeRender(req, res) {
const configPath = path.join(__appDirectory, 'myConfig.json')
const config = (await readFileAsync(configPath)).toString()
console.log(config)
}
Script can invoke rendering of another template. To do this you need to require
special module jsreport-proxy
and call render
function on it.
const jsreport = require('jsreport-proxy')
async function beforeRender(req, res) {
console.log('starting rendering from script')
const result = await jsreport.render({ template: { shortid: 'xxxxxx' } })
console.log('finished rendering with ' + result.content.toString())
}
Script can query the jsreport store and load an asset with config for example.
const jsreport = require('jsreport-proxy')
async function beforeRender(req, res) {
const assets = await jsreport.documentStore.collection('assets').find({name: 'myConfig'})
const config = JSON.parse(assets[0].content.toString())
req.data.config = config
}
The console
calls are propagated to the debug calls from the studio as well to the standard jsreport log.
function beforeRender(req, res) {
console.log('i\'m generating logs with debug level')
console.warn('i\'m generating logs with warn level')
console.error('i\'m generating logs with error level')
}
Add scripts
node to the standard config file:
"extensions": {
"scripts": {
"timeout": 30000,
"allowedModules": "*"
}
}
You can use standard OData API to manage and query script entities. For example you can query all scripts using
A custom script is physically linked to the stored report template using its shortid or name. In this cases there is nothing to do in the API call, because the script is automatically applied.
POST: { template: { name: 'My Template with script' }, data: { } }
If you are rendering anonymous template you can identify the script by its name or shortid
POST: {
template : {
content: "foo",
scripts: [{
shortid: "sd543fds"
}, {
name: "My Script"
}]
}
}
The last option is to specify the script content directly
POST: {
template : {
content: "foo",
scripts: [{
content: "function beforeRender(req, res, done) { req.template.content='hello'; done(); }"
}]
}
}
Some people prefer to push data into jsreport from the client and some people prefer to use scripts
extension to actively fetch them. Where pushing data to the jsreport is more straight forward, using scripts
can provide better architecture with fully separated reporting server where report itself is responsible for defining input as well as fetching them. The choice is up to you.