Since there are so many different options for when to run a transform script, it can be difficult to determine what will work best for your application. In this post, I’ll go over the different types of scripts as well as the variables that are available to each one. ServiceNow has some documentation on how to map with transform event scripts that I used as a reference while I was playing around with transform maps. I’ll just dive a little deeper into those concepts in this post. The following scripts are listed in the order that they execute in.
The following scripts are listed in the order that they execute in:
Below is a diagram of a sample import set. Each script will have another diagram that shows when they run.
On Start
Variables:Â source, import_set, map, log, ignore, error
Note: Only the variables listed above apply to the current script. A list of all the variables and how they work can be found at the bottom of the post.
Summary:Â onStart runs at the very beginning of the import before any of the records are processed. None of the fields can be accessed on the source or target tables since they have not been set up yet. If you try to access fields in the onStart script, the transform will not run. If you wanted to inform a user that more records were being imported, you could use the onStart script to send out the message that the import has started.
On Before
Variables:Â source, target, import_set, map, log, action, ignore, status_message, error, error_message
Summary: onBefore runs before every row is processed. This script runs for each individual record. If you want to set or alter values on the source table (see Temperature example explained in the Field Level Source Script section below) it can be done here. onBefore scripts can be used to verify that the value of a source field is valid. If the value is invalid, the script can take action.
Field Level Source Script
Variables:Â source, target, answer, map, log, action
Summary: The source script replaces the source field in the field map. It gives the target field a value. This is used when you want to change an individual field on each record. For example, if I had a Temperature field in my source table that was in Fahrenheit and I wanted it to be in Celsius on my target table, I would do the conversion in the field level source script. This will modify the temperature value for every record.
On Foreign Insert
Variables:Â source, target, map, log, action, name, value, ignore, error
Summary:Â onForeignInsert runs before a new referenced record is created. Since this script only runs on an insert, the choice action “create” has to be selected for that field map. If “create” is not selected, the script will never run.
At this point, we can access values on the source table. We also have access to the fields on the target table that have already been filled in. The one being transformed, however, will not yet have a value. onForeignInsert scripts can manage what happens when a new record is added to a referenced table. When I was playing around with onForeignInserts, I couldn’t find a way to change values on the referenced table from my script. There might be a way to do this, but I haven’t figured it out yet. An onAfter script might be a better option if that’s what you’re going for.
On Choice Create
Variables:Â source, target, map, log, action, name, value, ignore, error
Summary: onChoiceCreate runs before a new choice is created on a choice field. Since this script runs when a choice is created, “create” has to be selected as the choice action for that field map. If it is not selected, the script will not run. Because onChoiceCreate is a before action, only the source table record has set values. Similar to onForeignInsert, the onChoiceCreate script can take additional action when a new choice is created on a field.
On Reject
Variables:Â source, target, map, action, log
Summary:Â onReject runs before a record (often a referenced record) is rejected. In order for this script to run, “reject” has to be selected as the choice action for that field map.
Since onReject runs before the record is rejected, the value of the fields in the source table can still be accessed and stored. Once the script runs, the record is rejected and is not inserted into the target table. The onReject script is useful for gathering information about the rejected record and storing it or reporting it back to the user.
Record Level Script
Variables:Â source, target, map, log
Summary: Record level scripts run alongside the field maps as they transform fields from the source table to the target table. Both the target and source tables are accessible for this script. Since the script will execute for every record, the record level script is where you want to alter record values. If I had a Firstname and Lastname field on my source table and I wanted to merge them into a Name field on my target table, I would do that with the record level script since I have access to all the fields in both the source and target tables.
On After
Variables:Â source, target, import_set, map, log, action, status_message, error, error_message
Summary:Â onAfter runs after every row has been processed. Fields on the target table can be accessed from this point. They contain the value that was just inserted. At the time that this script runs, the source and target field values should be the same. With onAfter scripts, you can take the values of the fields that were just inputted and use them to fill out record fields on other tables.
On Complete
Variables:Â source, target, import_set, map, log, error
Summary: onComplete runs once the import has been completed and all records have been transformed. The script will only run once. As an example, you could use the onComplete script to send out a message saying that new records have just been added to the table.
Variables
There are many variables that can be used in transform scripts. Here is a list of all the variables and how they work:
action – returns “insert” or “update” depending on if the current record was or is being added or updated
error – a boolean that will stop the transformation and send an error message if set to true
error_message – the message sent if an error occurs
ignore – a boolean that will stop the transformation and ignore all following rows
import_set – the import set being transformed at that moment
log – function used for debugging the transformation (log.info(), log.error(), etc.)
map – information about the transform map record being processed
name – the name of the field on the target record that is currently being processed
source – A GlideRecord of the row being read in and processed
status_message – an output message sent in the XML response
target – A GlideRecord of the record being added to the target table
value – the display value of the field on the source record that is currently being processed
OnStart is an overlooked gem. It’s benefit is in setting up objects that persist through the entirety of the transform, each row and field transform, and all the way through to oncomplete.
This allows you to consolidate code and maintain it in one place. All other hooks can just call the method defined for it in OnStart or a script include initialized once in OnStart.
This also allows doing odd things like setting ignore=true if action=update, load updates into an object, then loop through and apply them async in oncomplete. You can also load reverse sync attributes into an object as you loop through the transforms, then drop it into a reverse sync queue to update fields in a source system if you doing something bidirectional.
It’s also nice to know I only have one script to read and maintain for the entire transform unless I’m adding parameters to the method that is called or something.
Thank you for the additional information! It’s always nice to learn from people who have more experience with ServiceNow.
Hi Aylee Andersen,
Thanks for the wonderfull post. I have tow queries, can you share your thought in it.
1) What is the scope and the result of the igonre variable (I mean where all the in the scripts I can use this, and what would be its impact).
2) Similarly the scope of error and also would like to know, how can I cancel the complete transformation.
Thanks a ton. Much useful. Excellent explanation of transform scripts. Now, I understood the differences between onAfter & onComplete.
Thanks so much for this. You just solved a really thorny problem for me.
Thank you so much for this post. One quick question on the onAfter script, does it trigger the business rule here? For example, I want to insert a new CI but associate it with an existing asset instead of creating a new one (BR does this)
Another interesting discovery.
A field map script on a field marked for coalesce will run three times.
First source is defined, but all values are undefined gs.nill(source.sys_id)===true. I believe source is a record set with all of the rows or maybe the query, but it’s not populated yet.
Then it will run once per-row, and before your onStart script. You can set status_message here, do queries etc.
Then onStart is run, I believe this sets up a new context that persists for the rest of the transforms, and either the rose in source are reset to the first of the query to iterate through them again, or they are looked up to populate the source object in the transform context.
Then the coalesce field map script runs again before the other per-row field_map scripts and before target is set. Then target is set and and other field map script run. This can have very useful behavior by setting sys_target_sys_id to your coalesce field. Just don’t set answer=-1 or you will need to call HI to have them remove a record with sys_id=-1.
With create new record on empty coalesce=false, and choice create set to yes, and empty string will give a row error, and undefined does an insert, but I prefer gs.generateGUID(). For one thing, at least in scope, the debugger shows an empty string as undefined, and empty vs undefined seems like the king of thing that would annoy a maintenance programmer.
Lots of interesting things can be done by creating persistent objects in onStart, and populating them as you walk through row level transforms, then step through post transform / per-row actions async in oncomplete.