# Report: How to modify and create custom report

**URL:** <https://developer.sailpoint.com/discuss/t/report-how-to-modify-and-create-custom-report/80726>\
**Category:** IIQ Community Knowledge Base\
**Tags:** reports, identityiq\
**Created:** [September 10, 2024, 12:31pm UTC](https://developer.sailpoint.com/discuss/t/report-how-to-modify-and-create-custom-report/80726 "2024-09-10T12:31:17Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![enistriminsait](https://avatars.discourse-cdn.com/v4/letter/e/58f4c7/32.png) [@enistriminsait](https://developer.sailpoint.com/discuss/u/enistriminsait)\
**Post date:** [September 10, 2024, 12:31pm UTC](https://developer.sailpoint.com/discuss/t/report-how-to-modify-and-create-custom-report/80726/1 "2024-09-10T12:31:17Z")

</div>

# Introduction

Report, the dark side of Sailpoint. At first sight, the reports on Sailpoint may seem difficult to create and manage and it is true, but they have their own logic that once understood will make everything more simple.

On this blog, I want to explain the structure, the main components of a report and how they interlace with each other. Finally, I want to share with the community a report template with a special configuration and with the possibility of adapting to all cases.

Report are a type of **taskDefinition** object and to build a report we two subtype of this object.  
The first is the **Template** , where is stored the logic of report: _what is the data and what do I do with it_.  
The second is the **LiveReport** , where the inputs are defined and how we want to see them. A LiveReport is an instance of a Template.

From a Template, we can generate multiple LiveReport with different characteristics:

 ![image](https://global.discourse-cdn.com/sailpoint/original/2X/6/60136c42e845bacbb575942dcb1497b7a4b5efb9.png)

# Template Structure

A template is composed of some part, where we can define on what data we want work, the inputs and we can rework the data during the generation of report.

The main part of a report are **Signature** , **RequiredRights** and **Attributes**.

In **Signature** we can define all input arguments, for example the values come from a Form.  
In **RequiredRights** we can associate the SPRights, to allow its visibility or not.  
In **Attributes** , the most important section, we define the source, the logic, the columns and if is present an input Form.

## DataSource & Parameters

There are 3 type of DataSource: **Filter** , **Java** and **HQL**.

### Filter

The most intuitive to use and recommended for beginners. We can say that it’s like writing a query in XML format.  
In this type, we define an objectType of Sailpoint like a principal data-subset and later we can filter or add other object (as long as they can be united). Example:

With this, we are taking all the identity class.

```auto
<DataSource defaultSort="application.name,value,identity.name" objectType="Identity" type="Filter">

```

Later we can add a filter or join other object with the paramaters.

```auto
<Join joinProperty="IdentityEntitlement.identity" property="name"/>
<QueryParameters>
    <Parameter argument="application" property="application.id"/>
<QueryParameters>

```

This configuration returns all the Identity values with the name of correlated IdentityEntitlement filter for the application/s received in input.

In this typology it is possible to use other type of filter like **Query\QueryScript** where you can write\compose a HQL query or **OptionsRule\OptionsScript** to assing a rule\script where you compose a **Filter**.

### Java

Maybe the most complex to use or to modify a report. With typology we can assing a Java class like source and use all its methods. Furthermore, all the attributes in the report and the signatures are passed to the class.

```auto
 <DataSource dataSourceClass="sailpoint.reporting.datasource.AccountAttributesLiveReportDataSource" objectType="Identity" type="Java">

```

For example, this source will work on the identities through **AccountAttributesLiveReportDataSource** class.

This typology can help us to provide reports quickly if the class already exist and have all the funcionality that we need or we can build ours class.  
On the other side, build a class requires time and if you want modify an existing report, is essential to know tthe class used.

### HQL

With this class of DataSource, we can write directly query based on HQL language. For all those who have experience in sql it will be very easy to understand and learn.  
Similar to **Filter** type, we can write a query and implement **Parameters** and **QueryScript** or we can insert the join and where into the query.

An example:

```auto
<Query> from Link l, Identity identity 
               left outer join identity.manager m
               where l.identity.id = identity.id and l.application.id in(:application)</Query>
  <QueryParameters>
    <Parameter argument="application" property="application"/>
  </QueryParameters>

```

This query return all the accoutn with relative identity and manager, filter by application.  
Also, we can remove `and l.application.id in(:application)` from query and implent a control on input paramater with a **QueryScript** :

```auto
<QueryScript>
    <Source>
        Object applications = args.get("application");
        if (applications != null && !applications.isEmpty()){
          query+= "and l.application.id in(:application)";
        }
        return query;
    </Source>
</QueryScript>

```

## Columns

Obviously, this section we declare all the columns that we want into the report and we can do static or dynamic. I am referring to the static column for the object **ReportColumnConfig** and to dynamic for the object **ExtendedColumnt**.  
**ReportColumnConfig** is static because every columns are declared like this, will always be present into the report even if it can be hidden.  
**ExtendedColumn** is dynamic because we can write a script that can create columns based on parameters and the result can change everytime.

### ReportColumnConfig

With a **ReportColumnConfig** object we define a static column, where we assign a property. A property is an attribute of any one object we are included in the **DataSource** , the lastname of **Identity** or the value of a **ManagedAttribute**.

```auto
<ReportColumnConfig field="Lastname" header="Lastname" property="identity.lastname" sortable="true" width="110"/>

```

We can work on the value of property, adding a rule or a script to printing an other value that depends from the property.

### ExtendedColumnScript & ExtendedColumnRule

This object allows us to build a dynamic columns schema, starting from the parameters that come from a **Form** for example. We can create columns based on the applications or on type of identities.

We can build columns with all attributes that we need, render script\rule too. In every case, the rutern of script\rule must be a list of **ReportColumnConfig** objects and they will be automatically added to the **Columns**.

About this I want to talk better in the next section where I will share an interesting template.

# My Custom Report

[Custom Report.xml](https://developer.sailpoint.com/discuss/uploads/short-url/bZbXx5KPqJixF8hsS4bNeMIy0n7.xml) (9.7 KB)

This is a interesting template with this configuration:

- DataSource: HQL
- Object in DataSource: Link, Identity, Manager, ManagedAttribute, IdentityEntitlement and Application
- Section: Form, ExtendedColumnScript, QueryParameters and QueryScript
- Input: List of Application amd optional query critiria

I will explain the most important part so that you can understand its flexibility and adapt it to your purposes.

## Form & Inputs

From the Form return a list of application and a string where the user can add an additional HQL filter. Both can be null.

```auto
<Argument name="queryCriteria" type="string"/>
<Argument multi="true" name="application" type="Application">
  <Description>rept_app_account_grp_memb_input_application_desc</Description>
  <Prompt>rept_app_account_grp_memb_input_application</Prompt>
</Argument>

```

## QueryParameters and QueryScript

Like parameter are present the argument `application`(comes from Form) and two default value for reading only aggregated ManagedAttribute and connected IdentityEntitlement.

```auto
<QueryParameters>
     <Parameter argument="application" property="application"/>
     <Parameter defaultValue="Connected" property="ie.aggregationState" valueClass="sailpoint.object.IdentityEntitlement$AggregationState"/>
     <Parameter defaultValue="true" property="ma.aggregated" valueClass="boolean"/>
</QueryParameters>

```

In the script I check the the arguments values and them to HQL query if they are not empty.

```auto
<QueryScript>
  <Source>
        String scriptCriteria ="";
        Object applications = args.get("application");
        if (applications != null && !applications.isEmpty()){
          scriptCriteria = " l.application.id in( :application ) ";
        }

        String addCriteria = args.get("queryCriteria");
        if (addCriteria!= null && !addCriteria.isEmpty()){
          if (!scriptCriteria.isEmpty())
            scriptCriteria+=" and ";
          scriptCriteria += addCriteria ;
        }

        if (!scriptCriteria.isEmpty())
          query+= " and " + scriptCriteria;
        return query;
    </Source>
</QueryScript>

```

## ExtendedColumnScript

About me this is the best part. The script read the list of application and serch on the application a custom object called **reportColumnMap**. This object is a map that contains a list of application attributes that I want to import into this report.  
We also provided that you can associate a rule to be inserted as renderRule or configure the column property with the Link or the Identity.

```auto
<ExtendedColumnScript>
  <Source> 
      import java.util.List;
      import java.util.Map;
      import sailpoint.reporting.*;
      import sailpoint.object.ReportColumnConfig;
      import sailpoint.object.Application;
      import sailpoint.object.Rule;

      List newCols = new ArrayList();
      Map formValues = form.getFieldValues();

      if (formValues != null && formValues.containsKey("application") && formValues.get("application") != null){
        String appid = formValues.get("application").get(0);
        Application application = context.getObjectById(Application.class,appid);
        Map simpleReportMap = application.getAttributeValue("reportColumnMap");

        if (simpleReportMap == null)
          throw new Exception("No column map defined for application " + application.getName());

        int i=0;
        for (String k : simpleReportMap.keySet()) {
          String fieldConf = simpleReportMap.get(k);
          if (fieldConf != null) {
            String field=null;
            String renderRuleName=null;
            ReportColumnConfig config = null;
            if (fieldConf.contains("#")) {
              field= fieldConf.split("#")[0];
              renderRuleName=fieldConf.split("#")[1];
            }
            else{
              field=fieldConf;
            ​}
            if (field.equals("_IDENTITY_")) {
              config = new ReportColumnConfig("identity"+ (i++) , k , "java.lang.String");
              config.setProperty("identity.id" );
            } else if (field.equals("_LINK_")) {
              config = new ReportColumnConfig("link"+ (i++) , k,"java.lang.String" );
              config.setProperty("l.id" );
            }else {
              config = new ReportColumnConfig("linkFieldSuffix"+ (i++) + field, k, "java.lang.String");
              config.setProperty(String.format("l.attributes.%s", field));
            }
            if (renderRuleName != null) {
                Rule renderRule = context.getObjectByName(Rule.class, renderRuleName);
                if (renderRule != null) {
                config.setRenderRule(renderRule);
              }
            }
            newCols.add(config);
          }​
        }
        ReportColumnConfig config = new ReportColumnConfig("userSource", "User Source", "java.lang.String");
        config.setProperty( "l.application.name" );
        newCols.add(config);
      }
      return newCols;
  </Source>
</ExtendedColumnScript>

```

This is a example of **reportColumnMap** :

```auto
<reportColumnMap>
    <String>Name</String>
    <String>_IDENTITY_</String>
    <String>_LINK_#RuleLink</String>
    <String>StartDate#RuleDate</String>
</reportColumnMap>

```

For the first the column will have **l.attributes.name** like property.  
The second will have **identity.id** like property(the id of identity) and we can managed with a script for example.  
The third will have “l.id” like property (the id of link) and RuleLink as RenderRule.  
The fourth will have **l.attributes.StartDate** like property and RuleDate as RenderRule.

# Conclusion

I hope this helps you create or modify reports that at first glance can be complicated and demoralizing. As always, the limit is your imagination.
