Examples

The following are some examples of how to use the Siren API to achieve different results.

Creating a "Hello World" script

You can create a very simple script as follows:

const version = 1;
const type = 'custom';
console.log("Hello World")

Accessing the current dashboard

You can get the object representing the current dashboard by using the following script:

sirenapi.Dashboard.getCurrentDashboard().then(dash => {
 // your code here
});

Registering an event handler

You can register an event handler when the count changes on a dashboard’s entity table by using the following script:

sirenapi.Dashboard.getCurrentDashboard().then(dash => {
  dash.on(sirenapi.Dashboard.EVENTS.SEARCH_COUNT_CHANGED, event => {
    // your code here
    // the event will give more information about which count was changed
    // as there could be multiple counts on a single dashboard (dashboard 360)
  });
});

Finding a visualization

You can find a visualization on the current dashboard by its title by using the following script:

const title = 'My visualization';
let vis;
const visualizations = dash.getVisualizationByTitle(title);
if (visualizations.length === 1) {
  vis = visualizations[0];
} else if (visualizations.length === 0) {
  throw new Error('Visualization "' + title + '" not found');
} else {
  throw new Error('More than one visualization with the title "' + title + '" found');
}

Finding a dashboard’s entity table

You can find a dashboard’s entity table by its title by using the following script:

const title = 'My search';
let mySearch;
const searches = dash.getDashboardDataModelSearchByTitle(title);
if (searches.length === 1) {
  mySearch = searches[0];
} else if (searches.length === 0) {
  throw new Error('Search "' + title + '" not found');
} else {
  throw new Error(
    'More than one node in dashboard datamodel with search "' + title + '"'. +
    'Use getDashboardDataModelSearchByNodeId instead'
  );
}

Using special variables

When the script is attached to a visualization, you can use a special variable currentVisualization to access the Visualization object representing visualization to which the script was attached.

You can also use the special variable currentDashboard to access the Dashboard object representing the current dashboard on which the script is executed.

const dash = currentDashboard;
const vis = currentVisualization;

Using methods registered by visualizations

Any custom method that is registered by a visualization is exposed on the Visualization object.

For example, a visualization called MapX registered a custom method called reloadMap(). The script is attached to an instance of the MapX visualization. The method can be accessed by using the following script:

...
const myMapX = currentVisualization;
myMapX.reloadMap();
...

Adding a filter to current filters

You can add a filter to current filters by using the following script:

...
const myFilter = {
  meta: {
    alias: "My filter"
  },
  match_all: {}
};
dash.addFilters(myFilter);
dash.applyState();
...

Replacing current filters

You can replace current filters by using the following script:

...
const filters = dash.filters;
...
// manipulate filters
...
dash.setFilters(filters);
dash.applyState();
...

Getting the current time of the dashboard

You can get the current time of the dashboard and create a time filter by using the following script:

...
const title = "my search";
const searches = dash.getDashboardDataModelSearchByTitle(title);
let mySearch;
if (searches.length === 1) {
  mySearch = searches[0];
} else if (searches.length === 0) {
  throw new Error('Search "' + title + '" not found');
} else {
  throw new Error(
    'More than one node in dashboard datamodel with search "' + title + '"'. +
    'Use getDashboardDataModelSearchByNodeId instead'
  );
}
const time = dash.getTime();
sirenapi.getTimeFilter(time, mySearch.indexPatternId)
.then(timeFilter => {
  // here we can use the timefilter created on correct field
})

Get any dashboard by id

You can get any dashboard by id by using the following script:

...
const dashboardId = 'dashboard:1';
const dashboardIdFilters = [
  {
    query: {
      term: {
        fieldA: 'valueA'
      }
    },
    meta: {
      alias: 'My filter'
    }
  }
]
sirenapi.Dashboard.fetchDashboardById(dashboardId)
.then(dash => {
  dash.setFilters(dashboardIdFilters);
  return dash.redirect();
})
.then(() => {
  console.log('After redirect');
})

Opening a dashboard

You can get any dashboard, manipulate its state and open it by using the following script:

...
async function redirectToDashboard() {
  const dashboardId = 'dashboard:1';
  const dash = await sirenapi.Dashboard.fetchDashboardById(dashboardId);
  dash.setQueryString('my new query')
  dash.setFilters([
    {
      query: {
        term: {
          fieldA: 'valueA'
        }
      },
      meta: {
        alias: 'My new filter'
      }
    }
  ])
  await dash.open();
}
...

Web services integration

You can integrate Web services by using the following script:

...
sirenapi.invokeWebService(groupName, serviceName, params, options)
.then(data => {

});
...

Building simple forms

The scripted panel API allows for a rapid creation of simple HTML forms.

The script below is an example of a form that takes two inputs and executes a function when the user presses a Submit button.

...
function onSubmit(formData) {
    console.log(formData);
}

const info = currentVisualization.getHtmlElement(
  '<div>' +
  '<p>Enter data and press the button.</p>' +
  '</div>'
);
const input1 = currentVisualization.getHtmlTextInputElement('name', 'Name', 'Enter your name', "");
const input2 = currentVisualization.getHtmlTextInputElement('itemsNo', 'Items Number', 'Enter number of items', "3");
const submit = currentVisualization.getHtmlSubmitButtonElement('Find');
const form = currentVisualization.getHtmlFormElement(onSubmit);
form.appendChild(info);
form.appendChild(input1);
form.appendChild(input2);
form.appendChild(submit);
currentVisualization.appendHtmlElement(form);

Results of the above script

image

Building an interactive table

One of the components provided by the scripted panel API is an interactive table.

The script below shows how this table is created, with a callback that handles rows being selected.

...
// Adds interactive table to panel
const columns = [
  { name: 'Column 1', field: 'field1' },
  { name: 'Column 2', field: 'field2' }
];
const data = [
  { field1: 'value1', field2: 'value2' },
  { field1: 'value3', field2: 'value4' }
];
table = currentVisualization.getInteractiveTable({ columns, data, onSelect });
currentVisualization.appendHtmlElement(table);

// Updates dashboard filter when rows are selected
function onSelect(selectedItems) {
  currentDashboard.setFilters([{
    meta: {
      alias: 'Selected items'
    },
    query: {
      bool: {
        minimum_should_match: 1,
        should: selectedItems.map(item => ({
          match_phrase: {
            'label': item.field1
          }
        }))
      }
    }
  }]);
  currentDashboard.applyState();
}

Results of the above script

image

Developing plug-ins that are compatibile with sirenAPI

First, you must register an interface with definitions of all custom methods that are used by your plug-in. This will tell the sirenAPI which versions of the API are supported by your plug-in.

import scriptingInterfaces from 'scripting-interfaces';
...
if ($injector.has('actionRegistry')) {
  const actionRegistry = $injector.get('actionRegistry');
  const apiVersion = '1';
  actionRegistry.registerInterfaceForType(
    apiVersion,
    'my_plugin_id',
    scriptingInterfaces.v1.visualizations.my_visualization
  );
}

To enable a visualization to interact with SirenAPI, a plug-in developer must register the required actions by using SirenAPI.

To register methods, use the actionRegister service in your visualization controller.

...
if ($injector.has('actionRegistry')) {
  const actionRegistry = $injector.get('actionRegistry');
  const apiVersion = '1';

  actionRegistry.register(apiVersion, $scope.vis.id, 'myMethod', async (param1, param2) => {
    // should ALWAYS return a promise !!!
  });
}
...

The plug-in developer can also emit custom events that scripts can listen to.

To emit a custom event, use the following script:

...
if ($injector.has('sirenAPI')) {
  const sirenAPI = $injector.get('sirenAPI');
  const data = {
    name: 'my-custom-event',
    ...
  }
  sirenapi.emit(sirenapi.EVENTS.CUSTOM, data);
};
...

In the script, you register event handlers and call methods as follows:

...
sirenapi.on(sirenapi.EVENTS.CUSTOM, event => {
  if (event.data.name === 'my-custom-event') {
    vis.myMethod();
  }
});
...

sirenAPI.es

The Elasticsearch client. For more information, see the Elasticsearch API reference documentation.

Example:

const { body } = await sirenapi.es.search({
  index: 'companies',
  // type: '_doc', // uncomment this line if you are using Elasticsearch ≤ 6
  body: {
    query: {
      match: { name: 'acme' }
  }
})

sirenAPI.Charts

In this script, you create a chart from ApexCharts and attach it to a scripted panel visualization.

The ApexCharts library provides many types of interactive charts. For more information, go to the ApexCharts website.

Example:

const options = {
  series: [44, 55, 41, 17, 15],
  labels: ['Comedy', 'Action', 'SciFi', 'Drama', 'Horror'],
  chart: {
    type: 'donut',
    width: 360
  }
};
sirenapi.Charts.createApexChart(options).then(chart => {
  currentVisualization.appendHtmlElement(chart.getHtmlElement());
  chart.render();
});

Results of the above script

image

Adding components built on top of React and Elastic UI

To enable this feature make sure the following configuration is enabled in investigate.yml
siren_scripting:
  librariesWhitelist: ['React', 'EUI']

The scripted panel API allows adding custom components writting using React library. This script below shows how to add a simple button.

...

const buttonStyle = {
  border: '1px solid red',
  padding: '10px',
  borderRadius: '2px'
}

// A react component
function SimpleButton() {
  const onClick = () => {
    console.log('button clicked');
  }
  return <div>
    <button style={buttonStyle} onClick={onClick}>Simple Button</button>
  </div>
}

currentVisualization.renderReactElement({
  Element: <SimpleButton/>
});

Results of the above script

image

React components from Elastic UI can also be used to build the custom components.

...
const {
  EuiButton
} = Eui;

// A react element wit Eui button
function SimpleEuiButton() {
  const onClick = () => {
    console.log('button clicked');
  }
  return <div>
    <EuiButton onClick={onClick}>Simple Button</EuiButton>
  </div>
}

currentVisualization.renderReactElement({
  Element: <SimpleEuiButton/>
});

Results of the above script

image

A more complex example to display the counts of the dashboard on a scripted panel.

const version = 1;
const type = 'custom';

const {
  EuiStat
} = Eui;

// This React component listens to SEARCH_COUNT_CHANGED event and display the count
function DisplayCounts(props) {
  const { dashboard } = props;
  const [count, setCount] = React.useState(0);

  React.useEffect(() => {
    dashboard.on(sirenapi.Dashboard.EVENTS.SEARCH_COUNT_CHANGED, e => setCount(e.count))
  }, [dashboard]);

  return <div>
    <EuiStat title={count} description={"Counts"} textAlign="right" />
  </div>
}

currentVisualization.renderReactElement({
  Element: <DisplayCounts
    dashboard={currentDashboard}
  />
});

Results of the above script

image

React components can also be shown in a modal over a dashboard or a visualization.

...

function ModalContentElement() {
  return <div>Modal Content</div>
}

// Open a modal over the visualization the script is attached to
currentVisualization.openModal(
  {
    Element: <ModalContentElement/>,
    titleText: 'Modal title',
    primaryText: 'Ok',
    onPrimaryClick: () => {},
    cancelText: 'Cancel',
    onCancel: () => {},
});


// Open a modal over the dashboard the script is attached to
currentDashboard.openModal(
  {
    Element: <ModalContentElement/>,
    titleText: 'Modal title',
    onPrimaryClick: () => {},
    cancelText: 'Cancel',
    onCancel: () => {}
});

Results of the above script

image