I have already created some code for retrieving that comments. bellow you can find the datasource and some columns from report.
The “comments” column contains comments taken from CertificationAction and for omments stored in IdentityHistoryItem, I created a separate column called “historyComments”. It is working, but I am not fully sure whether this is the correct or best approach to retrieve this information.
<DataSource dataSourceClass="sailpoint.reporting.datasource.CertificationLiveReportDataSource" objectType="CertificationItem" type="Java">
<QueryParameters>
<Parameter argument="phase" defaultValue="Staged" operation="NE" property="parent.certification.phase" valueClass="sailpoint.object.Certification$Phase"/>
<Parameter argument="type">
<QueryScript>
<Source>
import sailpoint.object.*;
queryOptions.addFilter(Filter.ne("parent.certification.type", Certification.Type.BusinessRoleComposition));
queryOptions.addFilter(Filter.ne("parent.certification.type", Certification.Type.AccountGroupPermissions));
return queryOptions;
</Source>
</QueryScript>
</Parameter>
<Parameter argument="tags" property="parent.certification.tags.id">
<QueryScript>
<Source>
import sailpoint.object.*;
import java.util.*;
if (value != null && !value.isEmpty()){
queryOptions.addFilter(Filter.containsAll("parent.certification.tags.id", value));
}
return queryOptions;
</Source>
</QueryScript>
</Parameter>
<Parameter argument="certificationGroups" property="parent.certification.certificationGroups.id"/>
<Parameter argument="applicationNames" property="applicationNames">
<QueryScript>
<Source>
import sailpoint.object.*;
import sailpoint.api.ObjectUtil;
List appNames = new ArrayList();
if (value != null){
appNames = ObjectUtil.convertIdsToNames(context, Application.class, value);
}
if (appNames.size() != 0){
queryOptions.addFilter(Filter.containsAll("applicationNames", appNames));
}
return queryOptions;
</Source>
</QueryScript>
</Parameter>
<Parameter argument="creationDate" property="parent.certification.created" valueClass="daterange"/>
<Parameter argument="signedDate" property="parent.certification.signed" valueClass="daterange"/>
<Parameter argument="expirationDate" property="parent.certification.expiration" valueClass="daterange"/>
</QueryParameters>
</DataSource>
<Columns>
<ReportColumnConfig field="nativeIdentity" header="rept_cert_col_account_id" property="exceptionEntitlements.nativeIdentity" sortable="true" width="110"/>
<ReportColumnConfig field="identityName" header="Account owner name" property="parent.identity" scriptArguments="parent.certification.type,targetName" width="110">
<RenderScript>
<Source>
import sailpoint.object.*;
if (value == null || Certification.Type.DataOwner.equals(scriptArgs.get("parent.certification.type"))){
return scriptArgs.get("targetName");
}
Identity myIdentity = context.getObjectByName(Identity.class, value);
result = myIdentity.getDisplayableName();
return result;
</Source>
</RenderScript>
</ReportColumnConfig>
<ReportColumnConfig field="identity" header="rept_cert_col_identity" property="parent.identity" scriptArguments="parent.certification.type,targetName" width="110">
<RenderScript>
<Source>
import sailpoint.object.*;
if (value == null || Certification.Type.DataOwner.equals(scriptArgs.get("parent.certification.type"))){
return scriptArgs.get("targetName");
}
return value;
</Source>
</RenderScript>
</ReportColumnConfig>
<ReportColumnConfig field="manager" header="rept_cert_col_manager_name" property="parent" width="110">
<RenderScript>
<Source>
import sailpoint.reporting.*;
return value != null ? ReportingLibrary.getCertificationItemManagerName(context, value) : null;
</Source>
</RenderScript>
</ReportColumnConfig>
<ReportColumnConfig field="signerName" header="Signer Name" property="parent.certification.signOffHistory.signerName" sortable="true" width="110">
<RenderScript>
<Source>
import sailpoint.object.*;
result = "";
if (value != null ){
Identity myIdentity = context.getObjectByName(Identity.class, value);
result = myIdentity.getDisplayableName();
}
return result;
// keep original header for field="signer" header="rept_cert_signoff_signer" (next field below)
</Source>
</RenderScript>
</ReportColumnConfig>
<ReportColumnConfig field="signer" header="Signer WorkerID" property="parent.certification.signOffHistory.signerName" sortable="true" width="110"/>
<ReportColumnConfig field="signed" header="rept_cert_signoff_signoff_date" property="parent.certification.signOffHistory.date" sortable="true" width="110"/>
<ReportColumnConfig field="esigned" header="rept_cert_signoff_esig" property="parent.certification.signOffHistory.electronicSign" sortable="true" width="110"/>
<ReportColumnConfig field="certName" header="rept_cert_col_cert_access_review" property="parent.certification.name" sortable="true" width="110"/>
<ReportColumnConfig field="certGroupName" header="rept_cert_col_cert_grp_name" property="parent.certification.certificationGroups.name" sortable="true" width="110"/>
<ReportColumnConfig field="tags" header="rept_cert_col_tags" property="parent.certification.tags.name" subQueryKey="id" width="110"/>
<ReportColumnConfig field="entitlementType" header="rept_cert_col_entitlement_type" property="type" sortable="true" width="110"/>
<ReportColumnConfig field="entitlement" header="rept_cert_col_entitlement_desc" property="exceptionEntitlements" scriptArguments="bundle,violationSummary,targetName" width="110">
<RenderScript>
<Source>
import sailpoint.reporting.*;
return ReportingLibrary.describeCertificationEntitlement(value, scriptArgs.get("bundle"), scriptArgs.get("violationSummary"), scriptArgs.get("targetName"));
</Source>
</RenderScript>
</ReportColumnConfig>
<ReportColumnConfig field="status" header="rept_cert_col_status" property="summaryStatus" sortable="true" width="110"/>
<ReportColumnConfig field="decision" header="rept_cert_col_decision" property="action.status" sortable="true" width="110"/>
<ReportColumnConfig field="newValue" header="rept_cert_col_newValue" property="action" width="110">
<RenderScript>
<Source>
import sailpoint.reporting.datasource.*;
return DataSourceUtil.getRemediationModifiableNewValue(value);
</Source>
</RenderScript>
</ReportColumnConfig>
<ReportColumnConfig field="remediationCompleted" header="rept_cert_col_revoke_completed" property="action" width="110">
<RenderScript>
<Source>
import sailpoint.reporting.ReportingLibrary;
import sailpoint.tools.Util;
import java.lang.String;
String status = ReportingLibrary.getRemediationStatus(context, value);
if (status != null && "yes".equalsIgnoreCase(status.trim())) {
return "Revoke Completed";
} else {
return Util.isNullOrEmpty(status) ? "" : status;
}
//return Util.isNullOrEmpty(status) ? "" : status;
</Source>
</RenderScript>
</ReportColumnConfig>
<ReportColumnConfig field="decisionMakerName" header="rept_cert_col_decision_maker" property="action.actorDisplayName" sortable="true" width="110"/>
<ReportColumnConfig field="decisionMaker" header="Decision Maker WorkerID" property="action.actorName" sortable="true" width="110"/>
<ReportColumnConfig field="comments" header="rept_cert_col_decision_maker_comments" property="action" width="110">
<RenderScript>
<Source>
import sailpoint.object.CertificationItem;
import sailpoint.object.CertificationEntity;
import sailpoint.object.CertificationAction;
result = null; //"certItemAction is null";
CertificationAction a = (CertificationAction) value;
if (a != null) {
result = null; //"certItemAction comment is null";
String c = a.getComments();
if (c != null && c.trim().length() > 0) return c;
}
CertificationItem item = null;
if (object instanceof CertificationItem) {
item = (CertificationItem) object;
} else if (row instanceof CertificationItem) {
item = (CertificationItem) row;
}
if (item != null) {
CertificationEntity e = item.getEntity();
if (e != null) {
result = null; //"cert entity action or comment is null";
CertificationAction ea = e.getAction();
if (ea != null) return ea.getComments();
}
}
return result;
</Source>
</RenderScript>
</ReportColumnConfig>
<ReportColumnConfig field="historyComments" header="Other Comments" property="id" width="110">
<RenderScript>
<Source>
import sailpoint.object.CertificationItem;
import sailpoint.object.CertificationLink;
import sailpoint.object.EntitlementSnapshot;
import sailpoint.object.Filter;
import sailpoint.object.IdentityHistoryItem;
import sailpoint.object.QueryOptions;
import sailpoint.tools.Util;
String comments = null;
Iterator iterator = null;
try {
if (value == null) {
return null;
}
CertificationItem certificationItem =
context.getObjectById(CertificationItem.class, (String) value);
if (certificationItem == null ||
certificationItem.getCertification() == null ||
certificationItem.getCertification().getId() == null) {
return null;
}
String certificationId = certificationItem.getCertification().getId();
String identityName = certificationItem.getIdentity();
if (identityName == null) {
return null;
}
String applicationName = null;
String nativeIdentity = null;
EntitlementSnapshot entitlementSnapshot =
certificationItem.getExceptionEntitlements();
if (entitlementSnapshot != null) {
applicationName = entitlementSnapshot.getApplicationName();
nativeIdentity = entitlementSnapshot.getNativeIdentity();
}
QueryOptions qo = new QueryOptions();
qo.addFilter(Filter.eq("type", "Comment"));
qo.addFilter(Filter.eq("identity.name", identityName));
iterator = context.search(IdentityHistoryItem.class, qo, "id");
StringBuffer buffer = new StringBuffer();
while (iterator != null && iterator.hasNext()) {
Object[] row = (Object[]) iterator.next();
if (row == null || row.length == 0 || row[0] == null) {
continue;
}
IdentityHistoryItem historyItem =
context.getObjectById(IdentityHistoryItem.class, (String) row[0]);
if (historyItem == null) {
continue;
}
CertificationLink certificationLink = historyItem.getCertificationLink();
if (certificationLink == null ||
!Util.nullSafeEq(certificationLink.getId(), certificationId)) {
continue;
}
if (applicationName != null &&
historyItem.getApplication() != null &&
!Util.nullSafeEq(historyItem.getApplication(), applicationName)) {
continue;
}
if (nativeIdentity != null &&
historyItem.getNativeIdentity() != null &&
!Util.nullSafeEq(historyItem.getNativeIdentity(), nativeIdentity)) {
continue;
}
String historyComment = historyItem.getHistoryComments();
if (historyComment != null) {
if (buffer.length() > 0) {
buffer.append(" | ");
}
buffer.append(historyComment);
}
}
if (buffer.length() > 0) {
comments = buffer.toString();
}
} finally {
if (iterator != null) {
Util.flushIterator(iterator);
}
}
return comments;
</Source>
</RenderScript>
</ReportColumnConfig>
<ReportColumnConfig field="application" header="rept_certification_form_application" property="exceptionEntitlements.application" sortable="true" width="110"/>
<ReportColumnConfig field="exclusionExplanation" header="rept_cert_col_exclusion_explanation" property="parent.certification" width="110">
<RenderScript>
<Source>
if (value != null){
List archivedEntities = value.fetchArchivedEntities(context);
if (archivedEntities != null
&& archivedEntities.size() > 0
&& archivedEntities.get(0) != null) {
return archivedEntities.get(0).getExplanation();
}
}
</Source>
</RenderScript>
</ReportColumnConfig>
<ReportColumnConfig field="recommendation" header="rept_cert_col_recommendation" property="recommendValue" scriptArguments="id" sortable="true" width="110">
<RenderScript>
<Source>
import sailpoint.reporting.ReportingLibrary;
return ReportingLibrary.getRecommendedDecision(context, value, scriptArgs.get("id"), locale, timezone);
</Source>
</RenderScript>
</ReportColumnConfig>
<ReportColumnConfig field="recommendationReasons" header="rept_cert_col_recommendation_reasons" property="id" width="110">
<RenderScript>
<Source>
import sailpoint.reporting.ReportingLibrary;
return ReportingLibrary.getRecommendationReasons(context, value, locale, timezone);
</Source>
</RenderScript>
</ReportColumnConfig>
<ReportColumnConfig field="recommendationTimestamp" header="rept_cert_col_recommendation_timestamp" property="id" width="110">
<RenderScript>
<Source>
import sailpoint.reporting.ReportingLibrary;
return ReportingLibrary.getRecommendationTimestamp(context, value, locale, timezone);
</Source>
</RenderScript>
</ReportColumnConfig>
</Columns>