Skip to content

Commit 16ae590

Browse files
Merge branch 'ServiceNowDevProgram:main' into SNTips
2 parents 4b2833d + 4523c2b commit 16ae590

File tree

35 files changed

+807
-0
lines changed

35 files changed

+807
-0
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// This example demonstrates how it is possible to see the generated SQL query without enabling the SQL debug feture in the navigator
2+
try {
3+
gs.trace(true);
4+
var incGr = new GlideRecord("incident");
5+
incGr.setLimit(10);
6+
incGr.orderByDesc("sys_created_on");
7+
incGr.query();
8+
// TODO any other logic comes here...
9+
}
10+
finally {
11+
gs.trace(false);
12+
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
There is a simple way, how the generated SQL query can be checked, without activating the SQL Debugger feature in the navigator.
2+
3+
Here you can see a small code snippet:
4+
```JS
5+
try {
6+
gs.trace(true);
7+
var incGr = new GlideRecord("incident");
8+
incGr.setLimit(10);
9+
incGr.orderByDesc("sys_created_on");
10+
incGr.query();
11+
}
12+
finally {
13+
gs.trace(false);
14+
}
15+
```
16+
By enabling the trace feature, the generated SQL query will be visible in the output:
17+
```SQL
18+
SELECT task0.`sys_id`
19+
FROM task task0
20+
IGNORE index(sys_created_on)
21+
WHERE task0.`sys_class_name` = 'incident'
22+
ORDER BY task0.`sys_created_on` DESC
23+
LIMIT 0, 10
24+
```
25+
26+
Another example:
27+
```JS
28+
try {
29+
gs.trace(true);
30+
var incGr = new GlideRecord("incident");
31+
incGr.setLimit(10);
32+
incGr.orderByDesc("sys_created_on");
33+
incGr.query();
34+
while (incGr.next()) {
35+
gs.info("Number: " + incGr.getValue("number") + " Created: " + incGr.getValue("sys_created_on"));
36+
}
37+
}
38+
finally {
39+
gs.trace(false);
40+
}
41+
```
42+
43+
```SQL
44+
SELECT task0.`sys_id`
45+
FROM task task0
46+
IGNORE index(sys_created_on)
47+
WHERE task0.`sys_class_name` = 'incident'
48+
ORDER BY task0.`sys_created_on` DESC
49+
LIMIT 0, 10
50+
```
51+
```SQL
52+
SELECT task0.`parent`,
53+
task0.`a_ref_3` AS `caused_by`,
54+
task0.`watch_list`,
55+
task0.`upon_reject`,
56+
task0.`sys_updated_on`,
57+
task0.`a_str_5` AS `origin_table`,
58+
task0.`approval_history`,
59+
task0.`skills`,
60+
task0.`number`,
61+
task0.`state`,
62+
task0.`sys_created_by`,
63+
task0.`knowledge`,
64+
task0.`order`,
65+
task0.`cmdb_ci`,
66+
task0.`delivery_plan`,
67+
task0.`impact`,
68+
task0.`contract`,
69+
task0.`active`,
70+
task0.`work_notes_list`,
71+
task0.`priority`,
72+
task0.`sys_domain_path`,
73+
task0.`rejection_goto`,
74+
task0.`business_duration`,
75+
task0.`group_list`,
76+
task0.`approval_set`,
77+
task0.`wf_activity`,
78+
task0.`universal_request`,
79+
task0.`short_description`,
80+
task0.`correlation_display`,
81+
task0.`work_start`,
82+
task0.`delivery_task`,
83+
task0.`additional_assignee_list`,
84+
task0.`a_int_1` AS `notify`,
85+
task0.`sys_class_name`,
86+
task0.`service_offering`,
87+
task0.`closed_by`,
88+
task0.`follow_up`,
89+
task0.`a_ref_7` AS `parent_incident`,
90+
task0.`a_ref_5` AS `reopened_by`,
91+
task0.`reassignment_count`,
92+
task0.`assigned_to`,
93+
task0.`variables`,
94+
task0.`sla_due`,
95+
task0.`comments_and_work_notes`,
96+
task0.`agile_story`,
97+
task0.`escalation`,
98+
task0.`upon_approval`,
99+
task0.`correlation_id`,
100+
task0.`made_sla`,
101+
task0.`a_int_6` AS `child_incidents`,
102+
task0.`a_int_8` AS `hold_reason`,
103+
task0.`task_effective_number`,
104+
task0.`a_ref_6` AS `resolved_by`,
105+
task0.`sys_updated_by`,
106+
task0.`opened_by`,
107+
task0.`user_input`,
108+
task0.`sys_created_on`,
109+
task0.`sys_domain`,
110+
task0.`route_reason`,
111+
task0.`a_int_4` AS `calendar_stc`,
112+
task0.`closed_at`,
113+
task0.`business_service`,
114+
task0.`a_str_11` AS `business_impact`,
115+
task0.`a_ref_2` AS `rfc`,
116+
task0.`time_worked`,
117+
task0.`expected_start`,
118+
task0.`opened_at`,
119+
task0.`work_end`,
120+
task0.`a_dtm_1` AS `reopened_time`,
121+
task0.`a_dtm_2` AS `resolved_at`,
122+
task0.`a_ref_4` AS `caller_id`,
123+
task0.`a_str_3` AS `subcategory`,
124+
task0.`work_notes`,
125+
task0.`a_str_7` AS `close_code`,
126+
task0.`assignment_group`,
127+
task0.`a_int_5` AS `business_stc`,
128+
task0.`a_str_10` AS `cause`,
129+
task0.`description`,
130+
task0.`a_str_2` AS `origin_id`,
131+
task0.`calendar_duration`,
132+
task0.`close_notes`,
133+
task0.`sys_id`,
134+
task0.`contact_type`,
135+
task0.`a_int_2` AS `incident_state`,
136+
task0.`urgency`,
137+
task0.`a_ref_1` AS `problem_id`,
138+
task0.`company`,
139+
task0.`activity_due`,
140+
task0.`a_int_3` AS `severity`,
141+
task0.`comments`,
142+
task0.`approval`,
143+
task0.`due_date`,
144+
task0.`sys_mod_count`,
145+
task0.`a_int_7` AS `reopen_count`,
146+
task0.`location`,
147+
task0.`a_str_1` AS `category`
148+
FROM task task0
149+
WHERE task0.`sys_class_name` = 'incident'
150+
AND task0.`sys_id` IN ('9060975f472d4210a53cdbe4116d4311',
151+
'9e7f9864532023004247ddeeff7b121f',
152+
'd71f7935c0a8016700802b64c67c11c6',
153+
'a9a16740c61122760004fe9095b7ddca',
154+
'd71b3b41c0a8016700a8ef040791e72a',
155+
'd7195138c0a8016700fd68449cfcd484',
156+
'd7158da0c0a8016700eef46c8d1f3661',
157+
'ef43c6d40a0a0b5700c77f9bf387afe3',
158+
'ef4225a40a0a0b5700d0b8a790747812',
159+
'a9e30c7dc61122760116894de7bcc7bd')
160+
```
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Executing this script would help the administrators to set all the inprogress update sets to complete state.
2+
3+
Most of the times this script comes in handy before setting up the instance to patching or upgradation as during that time updatesets need to be set complete and a backup has to be taken.
4+
5+
Be cautious while using this script as this sets all the update sets whose state is inprogress and name does not start with "default" to complete.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//note : this script is going to mark all the update sets that are in progress to complete so make sure the query meets your requirements.
2+
3+
var gr = new GlideRecord("sys_update_set"); //querying the update sets table to check update sets which are in progress
4+
gr.addEncodedQuery("state=in progress^nameNOT LIKEdefault");
5+
6+
gr.setValue("state","complete"); //marking them to complete and updating multiple records using updateMultiple()
7+
gr.updateMultiple();
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
var staleDays = 7;
2+
var closeDays = 14;
3+
var reminderGR = new GlideRecord('task');
4+
reminderGR.addActiveQuery();
5+
reminderGR.addEncodedQuery('sys_updated_onRELATIVELE@dayofweek@ago@' + staleDays);
6+
reminderGR.query();
7+
while (reminderGR.next()) {
8+
gs.eventQueue('task.reminder', reminderGR, reminderGR.assigned_to, staleDays + ' days without update.');
9+
}
10+
11+
var closeGR = new GlideRecord('task');
12+
closeGR.addActiveQuery();
13+
closeGR.addEncodedQuery('sys_updated_onRELATIVELE@dayofweek@ago@' + closeDays);
14+
closeGR.query();
15+
while (closeGR.next()) {
16+
closeGR.state = 3; // Closed
17+
closeGR.update();
18+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The script identifies tasks that haven’t been updated for a set period, sends reminder notifications to assigned users, and, if still inactive after additional time, automatically closes them. This helps keep task lists current and reduces manual follow-ups.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//To Encrypt password field
2+
var grIncident = new GlideRecord('incident');
3+
if (grIncident.get('dc1c4143476202101b589d2f316d4390')) {
4+
grIncident.setDisplayValue('u_pass', 'demo@123');
5+
grIncident.update();
6+
}
7+
//NOTE: You can't use the setValue() API for the Password2 field
8+
9+
//To print cipher text
10+
var grIncident = new GlideRecord('incident');
11+
if (grIncident.get('dc1c4143476202101b589d2f316d4390')) {
12+
gs.info('Encrypted cipher test of password ' + grIncident.getValue('u_pass'));
13+
}
14+
15+
//To decrypt password field
16+
var grIncident = new GlideRecord('incident');
17+
if (grIncident.get('dc1c4143476202101b589d2f316d4390')) {
18+
var result = grIncident.u_pass.getDecryptedValue();
19+
gs.info("Decrypted password- " +result);
20+
}
21+
//NOTE: The getDecryptedValue() API isn't scoped. It's available globally.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Dear ServiceNow Community,
2+
3+
4+
The GlideEncrypter API uses 3DES encryption standard with NIST 800-131 A Rev2 has recommended against using to encrypt data after 2023. ServiceNow offers alternative cryptographic solutions to the GlideEncrypter API.
5+
6+
Glide Element API to encrypt/decrypt password2 values through GlideRecord.
7+
8+
Below are the sample scripts I ran in my PDI: For Password fields.
9+
10+
Note: 'u_pass' is Password (2 Way Encrypted) field.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//For Non-password fields Encryption syntax using Key Management Framework Cryptographic module
2+
3+
var password = "Hello World";
4+
var encryptOp = new sn_kmf_ns.KMFCryptoOperation("global.vamsi_glideencrypter", "SYMMETRIC_ENCRYPTION")
5+
.withInputFormat("KMFNone");
6+
var encryptedText = encryptOp.doOperation(password); //Encrypting Hello world
7+
gs.info("After Encryption: " + encryptedText);
8+
9+
10+
//For Non-password fields Decryption syntax using Key Management Framework Cryptographic module
11+
12+
var encryptOp = new sn_kmf_ns.KMFCryptoOperation("global.vamsi_glideencrypter", "SYMMETRIC_DECRYPTION")
13+
.withOutputFormat("KMFNone");
14+
var clear_text = encryptOp.doOperation('91ddbb5d47c012101b589d2f316d438012p3lgrR72vEQW5yLk-WXKQ==aGqxYzUXuyLt3HTqcW6-HA=='); //Pass Cipher text of Hello World (Which is the output of first script)
15+
gs.info("After decryption: " + clear_text);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Generally when you want to encrypt or decrypt any Non-password fields earlier we have Glide Encrypter API methods for encryption and decryption.
2+
The GlideEncrypter API uses 3DES encryption standard with NIST 800-131 A Rev2 has recommended against using to encrypt data after 2023.
3+
ServiceNow offers alternative cryptographic (Key Management Framwork) solutions to the GlideEncrypter API.
4+
5+
Note: ServiceNow recommending to deprecate GlideEncrypter API with in the instances as soon as possible. The actual dead line is September 2025.
6+
7+
These are the sample scripts I ran in my PDI: For Non-password fields. I used AES 256 algorithm for Symmetric Data Encryption/Decryption.
8+
9+
To test the scripts you need to create Cryptographic module and generate the key.
10+
11+
"global.vamsi_glideencrypter" is my cryptographic module name.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Overview
2+
This code snippet helps ServiceNow developers manage group memberships automatically by integrating with an external API. It retrieves group membership data from a specified API endpoint and updates user-group relationships in ServiceNow accordingly.
3+
4+
This is useful for organizations where user groups are managed dynamically in external systems, and developer want a seamless and up-to-date integration with ServiceNow.
5+
6+
# How It Works
7+
The script:
8+
- Fetches API Data: It connects to an external API (specified by the `apiEndpoint` variable) to retrieve the current group membership details.
9+
- Parses API Response: The response is parsed to extract user information (based on email) and group identifiers.
10+
- Updates Group Memberships:
11+
- For each member in the response, the script queries the `sys_user` table to locate the user in ServiceNow based on their email address.
12+
- Once a user is found, the script creates a new record in the `sys_user_grmember` table, associating the user with the appropriate group.
13+
14+
# Implementation
15+
- Define the `apiEndpoint` URL, replacing `https://your-group-api.com/members` with the actual endpoint from which group membership data will be fetched.
16+
- Ensure that any necessary authentication for the API is configured, such as API keys or tokens.
17+
- This script uses email as a unique identifier for users. Adjust `userGR.addQuery('email', member.email)`; if another identifier is needed.
18+
- Deploy the script as a Business Rule in ServiceNow, setting the appropriate table and conditions under which it should execute. For example, it could run on a schedule or be triggered by a specific update.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Script to update group memberships based on API data
2+
(function executeRule(current, previous /*null when async*/) {
3+
var apiEndpoint = 'https://your-group-api.com/members';
4+
var request = new sn_ws.RESTMessageV2();
5+
request.setEndpoint(apiEndpoint);
6+
request.setHttpMethod('GET');
7+
8+
var response = request.execute();
9+
var responseData = JSON.parse(response.getBody());
10+
11+
// Update group memberships
12+
responseData.members.forEach(function(member) {
13+
var userGR = new GlideRecord('sys_user');
14+
userGR.addQuery('email', member.email);
15+
userGR.query();
16+
17+
if (userGR.next()) {
18+
var groupMembership = new GlideRecord('sys_user_grmember');
19+
groupMembership.initialize();
20+
groupMembership.group = member.group_id;
21+
groupMembership.user = userGR.sys_id;
22+
groupMembership.insert();
23+
}
24+
});
25+
})(current, previous);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Overview
2+
This ServiceNow script automates backing up critical record data (such as task or incident records) to an external storage solution. Designed to run as a Business Rule, it helps maintain redundancy for sensitive information by copying specific record details to a backup API whenever a record is created or modified.
3+
4+
# How It Works
5+
- Data Extraction: Collects key record fields (such as `sys_id`, `number`, `short_description`) from `current`.
6+
- API Call: Sends a `POST` request with record data to an external backup endpoint.
7+
- Logging: Outputs API response for monitoring.
8+
9+
# Implementation
10+
1. Update the `setEndpoint` URL to match your backup API endpoint.
11+
2. Modify the `recordData` with table data structure as needed.
12+
3. Ensure the Business Rule is triggered on the appropriate conditions (e.g., on record insert/update) in the target table.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Script to back up critical table data to external storage
2+
(function executeRule(current, previous /*null when async*/) {
3+
var recordData = {
4+
sys_id: current.sys_id.toString(),
5+
number: current.number.toString(),
6+
short_description: current.short_description.toString()
7+
};
8+
9+
// Call external API to store data
10+
var request = new sn_ws.RESTMessageV2();
11+
request.setEndpoint('https://your-backup-api.com/backup');
12+
request.setHttpMethod('POST');
13+
request.setRequestBody(JSON.stringify(recordData));
14+
15+
var response = request.execute();
16+
gs.info("Backup response: " + response.getBody());
17+
})(current, previous);
18+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Conditional Form Sections Based on User Role
2+
3+
## Overview
4+
This client script dynamically shows or hides specific sections of a form based on the logged-in user’s role. It ensures that only authorized users, such as managers, can view and interact with sensitive sections (e.g., budget approvals).
5+
6+
## Use Case
7+
- Managers: Can see the Budget Approval section.
8+
- Other Users: The section remains hidden.
9+
10+
## How It Works
11+
- The script runs onLoad of the form.
12+
- It checks if the logged-in user has the specified role (manager in this example).
13+
- If the user has the role, the Budget Approval section is shown. If not, it remains hidden.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
function onLoad() {
2+
var userHasRole = g_user.hasRole('case_manager'); // Check if user has specific role
3+
4+
if (userHasRole) {
5+
g_form.setSectionDisplay('budget_approval', true); // Show section if user has specific role
6+
} else {
7+
g_form.setSectionDisplay('budget_approval', false);
8+
}
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Display Section Based on State
2+
3+
## Overview
4+
This client script dynamically displays specific sections on a form based on the selected State field value. It improves user experience by hiding or showing relevant sections as needed.
5+
6+
## How It Works
7+
- When the State field changes, the script checks the new value.
8+
- For Example, if the state is changed to "Resolved", the Resolution Section becomes visible.
9+
- For other states, the section remains hidden.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
2+
if (isLoading || newValue === '') {
3+
return;
4+
}
5+
6+
if (newValue == 6) //State on which section needs to be displayed
7+
g_form.setSectionDisplay('resolution_information', true); //Section which needs to be display
8+
else
9+
g_form.setSectionDisplay('resolution_information', false);
10+
}

0 commit comments

Comments
 (0)