cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Running a rule from an EmailTemplate

Running a rule from an EmailTemplate

Velocity Template Language (VTL) that we use to make the email notifications dynamic is very powerful, but it also takes a lot of effort and could sometimes take quite a bit of "trial and error" to get it all right. The syntax can be cumbersome and we do not always have the power that a programming language like Java or the BeanShell scripting language provides, especially if it comes to instantiating objects with parameters.

 

In a customer situation, I had to determine whether a work item was assigned directly to an identity (the owner), or that the original identity had a forward preference causing the work item to be assigned to the eventual owner.

 

To do so, I needed to instantiate the class sailpoint.api.Workflower and call the checkForward(...) method on that object. This turned out to be next to impossible. It is possible, however to get hold of an instance of the SailPointContext. Then using the SailPointContext, retrieve a Rule object and run that rule.

 

That means we can do even more magic! Rules can do just about anything! So, we can now more easily calculate things and return the outcome to a variable in the EmailTemplate.

 

So, how does this work? Well, first of all, we need to get hold of that SailPointContext. For that, see this blog post.

 

#set($spctx=$spTools.class.forName("sailpoint.api.SailPointFactory").getMethod("getFactory", null).invoke(null, null).getCurrentContext())

 

Then, we need to get the Rule object, for example "Example - My EmailTemplate Rule":

 

#set( $rule = $spctx.getObjectByName($spTools.class.forName("sailpoint.object.Rule"), "Example - My EmailTemplate Rule") )

 

Before we can run the rule, we need to set the rule argument as a List object. Let's assume that we just want the $identityName attribute to be provided as input to the rule. To add items to the list, we need to call the add() method on the List object. This however returns output, which would be printed in the template. To prevent that, we assign that output to a dummy variable.

 

#set( $ruleArgs=$spTools.class.forName("java.util.HashMap").newInstance() )
#set( $dummy = $ruleArgs.put( "identityName", $identityName ) )

 

Then we call the rule and assign the output of the rule to a new variable:

 

#set( $ruleOutput = $spctx.runRule($rule, $ruleArgs) )

 

The output of the rule can now be used in the template to make decisions, or to be part of the template.

 

Unfortunately, this trick will take a bit too many characters to do anything advanced in Subject, Cc and Bcc fields, but for the body, it's very useful.

Labels (1)
Comments

Thanks for this post Menno!

It was very useful in my use case of notifying a user's manager whenever a new work item comment is added on certain approvals.

I did need to alter this:

#set( $rule = $spctx.getObjectByName($spTools.class.forName("sailpoint.object.Rule"), "Example - My EmailTemplate Rule");

In order to remove the semicolon and add an extra closing parentheses:

#set( $rule = $spctx.getObjectByName($spTools.class.forName("sailpoint.object.Rule"), "Example - My EmailTemplate Rule") )

Cheers!

Thanks, I corrected that!

Hi,

I want to fetch application revoker in email template, for that i will have get the application object.

Can you please tell me how to fetch application object and application revoker in email template if i have workitem.

Thank you.

...just from the top of my mind:

#set($spctx=$spTools.class.forName("sailpoint.api.SailPointFactory").getMethod("getFactory", null).invoke(null, null).getCurrentContext()) 

#set( $app = $spctx.getObjectByName($spTools.class.forName("sailpoint.object.Application"), "My Application") )

#set( $revokers = $app.getRemediators() )

Note that the revoker is called "remediator" under the hood and is a list, even though only one value can be entered in the user interface.

- Menno

Hello,

I tried doing this but I seem to be having an issue getting the value that is in my rule arguments map to go over to the rule. Every time I run this, the value in the rules is null, even though I'm printing $item.displayValue in the email itself, so I know its not null. Can you see anything I'm doing wrong? The email template that this is used in is a part of our LCM process and it sends the requester notification email. The reason I need to have some special processing in here is because when you remove someone from a group in ServiceNow, the ServiceNow group guid is only shown in the email and in the approval item. I want to display the actual group name. So I created a rule that does that lookup but I just need to be able to pass the ServiceNow guid value over to the rule to run and I can't seem to get that right. The rule itself runs but the value is just null.

#set($spctx=$spTools.class.forName("sailpoint.api.SailPointFactory").getMethod("getFactory", null).invoke(null, null).getCurrentContext())

#set( $rule = $spctx.getObjectByName($spTools.class.forName("sailpoint.object.Rule"), "LCM - Get ServiceNow Group Name") )

#set( $ruleArgs=$spTools.class.forName("java.util.HashMap").newInstance() )

#set( $dummy = $ruleArgs.put( "groupId", $item.displayValue ) )   

#set( $dynamicGroupNameContent = $spctx.runRule( $rule, $ruleArgs ) )

Thanks,

Amy

Hi Amy,

You could try to see what is in $ruleArgs just before you run the rule. Also the debug example ​ may be of use.

- Menno

I have also another solution to execute some custom methods in your emailtemplate body. Write a custom class f.e.EmailMethodLibrary , containing all the methods you need, and put it in sailpoint.services.task folder. In the body of the emailTemplate, start with : #set($c=$spTools.class.forName("sailpoint.services.task.EmailMethodLibrary").getMethod("getInstance",null).invoke(null,null)) and then wherever in the body, you can call your methods exemple (where getAccountName is a method in the custom class) : #set($accountname=$c.getAccountName($item.nativeIdentity)) I still have to try it, but maybe it is even possible to use in cc, as its length is more acceptable exemple to retrieve some aditional email adresses based on data in the certification object, by calling a custom method getCC : cc="$spTools.class.forName('sailpoint.services.task.EmailMethodLibrary').getMethod('getInstance',null).invoke(null,null).getCC($certification)"
Sally_Newton

Oh, please, don't use logic in a template engine

Thanks for this post Menno!

I have a need to pass the defined variables to the subject line of email template but unable to fetch the value even though the value was getting printed properly in email body.

Also I tried to mention the same logic used to define the variables inside the <Subject> tag of the email template in order to fetch the value, but getting some character limitation issue. Please find the below logs for reference. Kindly help me in passing the variable value to the email subject.

 

Caused by: org.hibernate.exception.DataException: could not update: [sailpoint.object.EmailTemplate#059d90036e2a8521016e60f22d6c7c6e]
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:102)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2596)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2478)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2805)
at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:114)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:268)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:260)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:180)
at sailpoint.persistence.PatchedDefaultFlushEventListener.performExecutions(PatchedDefaultFlushEventListener.java:39)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1206)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:375)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137)
at sailpoint.persistence.HibernatePersistenceManager.commitTransaction(HibernatePersistenceManager.java:484)
... 69 more
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: String or binary data would be truncated.
at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(SQLServerException.java:216)
at com.microsoft.sqlserver.jdbc.SQLServerStatement.getNextResult(SQLServerStatement.java:1515)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.doExecutePreparedStatement(SQLServerPreparedStatement.java:404)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement$PrepStmtExecCmd.doExecute(SQLServerPreparedStatement.java:350)
at com.microsoft.sqlserver.jdbc.TDSCommand.execute(IOBuffer.java:5696)
at com.microsoft.sqlserver.jdbc.SQLServerConnection.executeCommand(SQLServerConnection.java:1715)
at com.microsoft.sqlserver.jdbc.SQLServerStatement.executeCommand(SQLServerStatement.java:180)
at com.microsoft.sqlserver.jdbc.SQLServerStatement.executeStatement(SQLServerStatement.java:155)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.executeUpdate(SQLServerPreparedStatement.java:314)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
at org.hibernate.jdbc.NonBatchingBatcher.addToBatch(NonBatchingBatcher.java:46)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2574)
... 81 more

 

Thanks & Regards,

Ruba

@menno_pieters  thanks for this post! I was wondering if there is a way to call methods from the sailpoint.tools.Util library from within the email templates?

Version history
Revision #:
3 of 3
Last update:
‎Mar 24, 2023 07:44 PM
Updated by: