Skip to content

Commit cea800d

Browse files
authored
Record activity collector (#889)
1 parent 4357dd5 commit cea800d

File tree

2 files changed

+316
-0
lines changed

2 files changed

+316
-0
lines changed
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
var ActivityStreamCollector = Class.create();
2+
ActivityStreamCollector.prototype = {
3+
initialize: function(tableName, recordSysId) {
4+
this.tableName = tableName;
5+
this.recordSysId = recordSysId;
6+
7+
this.arrayUtil = new global.ArrayUtil();
8+
this.users = {};
9+
},
10+
11+
/**
12+
* This function collects the activity elements of a record and gives back the result as a JSON array
13+
*/
14+
getActivityEntries: function() {
15+
16+
var historyArray = [];
17+
18+
var hw = new sn_hw.HistoryWalker(this.tableName, this.recordSysId);
19+
// Get the first (original element)
20+
hw.walkTo(0);
21+
// This record is used for collecting the initial field values
22+
var currentRec = hw.getWalkedRecord();
23+
24+
// As a first step we need to know which audited fields are shown on the record's form
25+
var activityFields = gs.getProperty('glide.ui.' + this.tableName + '_activity.fields', "");
26+
// This definition is a comma separated list, which we need to convert as an array
27+
activityFields = activityFields.split(",");
28+
29+
// Collect the initial data
30+
var initialHistoryData = this._getFirstHistory(currentRec, activityFields);
31+
32+
// Walking through the history data
33+
while (hw.walkForward()) {
34+
var nextRec = hw.getWalkedRecord();
35+
36+
// Get all field changes
37+
var fieldChanges = this._compareTwoRecords(currentRec, nextRec, activityFields);
38+
if (fieldChanges.fields.length > 0)
39+
historyArray.push(fieldChanges);
40+
41+
currentRec = hw.getWalkedRecordCopy();
42+
}
43+
44+
// Get Comments and Work Notes
45+
historyArray = this.arrayUtil.concat(historyArray, this._getNotesComments());
46+
47+
// Get attachments
48+
historyArray = this.arrayUtil.concat(historyArray, this._getAttachments());
49+
50+
// Sort the element by date desc.
51+
historyArray.sort(function(elem1, elem2) {
52+
var date1 = new GlideDateTime(elem1.date);
53+
var date2 = new GlideDateTime(elem2.date);
54+
if (date1 > date2) {
55+
return -1;
56+
} else if (date1 < date2) {
57+
return 1;
58+
} else {
59+
return 0;
60+
}
61+
});
62+
63+
// Add initial data to the end of array
64+
// It is handled as a last step, because if the date of this activity is the same with the first action, de order is not correct.
65+
historyArray.push(initialHistoryData);
66+
67+
var historyDataArray = [];
68+
for (var idx in historyArray) {
69+
var currentActivity = historyArray[idx];
70+
71+
var activityValue;
72+
73+
if (currentActivity.type == 'field_changes') {
74+
var fields = [];
75+
76+
for (var idxElem in currentActivity.fields) {
77+
var currentElem = currentActivity.fields[idxElem];
78+
fields.push({
79+
label: currentElem.label,
80+
oldValue: currentElem.oldValue,
81+
newValue: currentElem.newValue
82+
});
83+
}
84+
85+
activityValue = fields;
86+
} else if (currentActivity.type == 'attachment') {
87+
activityValue = currentActivity.fileName;
88+
} else {
89+
activityValue = currentActivity.text;
90+
}
91+
92+
historyDataArray.push({
93+
createdBy: currentActivity.userName,
94+
createdOn: currentActivity.date,
95+
type: currentActivity.type,
96+
value: activityValue
97+
});
98+
}
99+
return historyDataArray;
100+
},
101+
102+
103+
/**
104+
* Get all attachments, which are related to the current record
105+
*/
106+
_getAttachments: function() {
107+
var attachments = [];
108+
var saGr = new GlideRecord('sys_attachment');
109+
saGr.addQuery("table_name", this.tableName);
110+
saGr.addQuery("table_sys_id", this.recordSysId);
111+
saGr.query();
112+
while (saGr.next()) {
113+
attachments.push({
114+
date: saGr.getDisplayValue("sys_created_on"),
115+
userName: this._getUserDisplayName(saGr.getValue("sys_created_by")),
116+
type: "attachment",
117+
fileName: saGr.getValue("file_name")
118+
});
119+
}
120+
return attachments;
121+
},
122+
123+
/**
124+
* This function collects and gives back the audited fields which are shown on the activity stream
125+
*/
126+
_getFirstHistory: function(currGr, activityFields) {
127+
var fieldDataArray = [];
128+
129+
for (var idxItem in activityFields) {
130+
var item = activityFields[idxItem];
131+
132+
try {
133+
134+
// In case of Work notes and Comments we can skip the process
135+
if (item == "work_notes" || item == "comments")
136+
continue;
137+
138+
var currentRecElem = currGr.getElement(item);
139+
// In case of no value it can be skipped
140+
if (gs.nil(currGr.getDisplayValue(item)))
141+
continue;
142+
143+
// Add the object to the array
144+
fieldDataArray.push({
145+
name: item,
146+
label: currentRecElem.getLabel(),
147+
oldValue: "",
148+
newValue: currGr.getDisplayValue(item),
149+
});
150+
} catch (err) {} // In order to handle some unexpected cases...
151+
}
152+
153+
return {
154+
date: currGr.getDisplayValue("sys_created_on"),
155+
userName: this._getUserDisplayName(currGr.getValue("sys_created_by")),
156+
type: "field_changes",
157+
fields: fieldDataArray
158+
};
159+
},
160+
161+
/**
162+
* This function compares two records and gets all audited fields, where the content is different
163+
*/
164+
_compareTwoRecords: function(currGr, nextGr, activityFields) {
165+
166+
var fieldDataArray = [];
167+
168+
for (var idxItem in activityFields) {
169+
var item = activityFields[idxItem];
170+
var currentRecElem = currGr.getElement(item);
171+
var nextRecElem = nextGr.getElement(item);
172+
173+
if (currentRecElem == nextRecElem)
174+
continue;
175+
176+
fieldDataArray.push({
177+
name: item,
178+
label: currentRecElem.getLabel(),
179+
oldValue: currGr.getDisplayValue(item),
180+
newValue: nextGr.getDisplayValue(item),
181+
});
182+
}
183+
184+
return {
185+
date: nextGr.getDisplayValue("sys_updated_on"),
186+
userName: this._getUserDisplayName(nextGr.getValue("sys_created_by")),
187+
type: "field_changes",
188+
fields: fieldDataArray
189+
};
190+
},
191+
192+
/**
193+
* Get Work notes, Comments from a record
194+
*/
195+
_getNotesComments: function() {
196+
197+
var noteArray = [];
198+
199+
var journalGr = new GlideRecord("sys_journal_field");
200+
journalGr.addQuery("name", this.tableName);
201+
journalGr.addQuery("element_id", this.recordSysId);
202+
journalGr.query();
203+
while (journalGr.next()) {
204+
noteArray.push({
205+
date: journalGr.getDisplayValue("sys_created_on"),
206+
userName: this._getUserDisplayName(journalGr.getValue("sys_created_by")),
207+
type: journalGr.getDisplayValue("element"),
208+
text: journalGr.getValue("value"),
209+
});
210+
}
211+
212+
return noteArray;
213+
},
214+
215+
/**
216+
* Get user display name
217+
*/
218+
_getUserDisplayName: function(userName) {
219+
220+
// A bit caching...
221+
if (this.users[userName])
222+
return this.users[userName];
223+
224+
var userGr = new GlideRecord("sys_user");
225+
if (userGr.get("user_name", userName)) {
226+
this.users[userName] = userGr.getDisplayValue("name");
227+
return this.users[userName];
228+
} else
229+
return "";
230+
},
231+
232+
type: 'ActivityStreamCollector'
233+
};
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
It might be a requirement when we need to collect the activities from a ticket, and report it to any purposes. There is an OOTB available Processor, called NGListHistoryDirectProcessor, but it executes a Java code, and call it from the back-end can be complex. So this solution makes the activity collection simple.
2+
3+
The feature can be used easily:
4+
5+
``` Javascript
6+
var tableName = "incident";
7+
var recordSysId = "57af7aec73d423002728660c4cf6a71c";
8+
var collector = new global.ActivityStreamCollector(tableName, recordSysId);
9+
var activityObj = collector.getActivityEntries();
10+
gs.info(JSON.stringify(activityObj));
11+
```
12+
13+
The result will be a JSON array, which contains the history of record like it can be seen on the form:
14+
15+
``` JSON
16+
[
17+
{
18+
"createdBy": "System Administrator",
19+
"createdOn": "2023-10-17 17:28:14",
20+
"type": "field_changes",
21+
"value": [
22+
{
23+
"label": "Incident state",
24+
"oldValue": "New",
25+
"newValue": "In Progress"
26+
}
27+
]
28+
},
29+
{
30+
"createdBy": "System Administrator",
31+
"createdOn": "2023-10-17 17:05:18",
32+
"type": "comments",
33+
"value": "Test comment"
34+
},
35+
{
36+
"createdBy": "System Administrator",
37+
"createdOn": "2023-09-14 21:38:02",
38+
"type": "attachment",
39+
"value": "test.pdf"
40+
},
41+
{
42+
"createdBy": "System Administrator",
43+
"createdOn": "2018-12-13 08:30:24",
44+
"type": "work_notes",
45+
"value": "Changed the priority of the Incident"
46+
},
47+
{
48+
"createdBy": "System Administrator",
49+
"createdOn": "2018-08-30 10:06:52",
50+
"type": "field_changes",
51+
"value": [
52+
{
53+
"label": "Incident state",
54+
"oldValue": "",
55+
"newValue": "New"
56+
},
57+
{
58+
"label": "Impact",
59+
"oldValue": "",
60+
"newValue": "3 - Low"
61+
},
62+
{
63+
"label": "Priority",
64+
"oldValue": "",
65+
"newValue": "4 - Low"
66+
},
67+
{
68+
"label": "Opened by",
69+
"oldValue": "",
70+
"newValue": "System Administrator"
71+
},
72+
{
73+
"label": "Caller",
74+
"oldValue": "",
75+
"newValue": "David Miller"
76+
}
77+
]
78+
}
79+
]
80+
```
81+
82+
And finally the image below represents how the activity information looks like on the form:
83+
![activities](https://github.com/manrick/code-snippets/assets/29863465/345256f6-7e18-49b8-acc1-9e5d4c68131e)

0 commit comments

Comments
 (0)