Extending a Script Include Not Working

sarah_kovar
Tera Contributor

Hello, 

I am trying to extend the script include 'SCPopularItems'. This script include is not read only, but there is only one method contained in it that I want to override. We'd like to leave 'SCPopularItems' OOB so we can continue to receive updates to it during upgrades. 

I've looked at two official docs from ServiceNow on this as well as countless Community articles. 

Right now the only difference (for testing purposes) between 'SCPopularItems' and 'MySCPopularItems' is I've placed a gs.log statement within MySCPopularItems to see when it's triggered. I do have additional customizations to it that I will place, but wanted to just prove out whether it will override the method contained within the OOB script include.

Can anybody tell me why this isn't working?
I've tried uncommenting and commenting the three lines of code for 'initialize: function()...'  

 

New script include name: 'MySCPopularItems'

var MySCPopularItems = Class.create();

MySCPopularItems.prototype = Object.extendsObject(SCPopularItems, {

    // initialize: function() {
    //     SCPopularItems.prototype.initialize.call(this);
    // },

    _getFromChannelAnalytics: function() {
        gs.log("SBK MySCPopularItems _getFromChannel called"); //only customization to this method at this time
        var itemsResult = [];
        var citm = 0;
        var count = new GlideAggregate('catalog_channel_analytics');
        count.setCategory('catalog_meta');
        count.setGroupByFollowRef(false);
        count.setOrderByFollowRef(false);
        count.addAggregate('COUNT''catalog_item');
        count.groupBy('catalog_item');

        count.addEncodedQuery(this._basicQueryFilters[this._baseQuery] || '');
        if (Array.isArray(this._allowedItemsList))
            count.addQuery('catalog_item'"IN"this._allowedItemsList);

        count.addNotNullQuery('catalog_item');
        // Donot Include items ordered as part of orderguide
        count.addNullQuery('order_guide');
        count.addNotNullQuery('document_key');

        count.orderByAggregate('COUNT''catalog_item');
        count.query();
        while (citm < this._itemsLimit && count.next()) {
            var catItemId = count.getValue("catalog_item");
            var catalogItemJS = new sn_sc.CatItem(catItemId);
            var catItemDetails = this._itemSummaryFetcher(catalogItemJS);
            if (!this._allowItem(catalogItemJS, catItemDetails))
                continue;
            if (this._itemValidator(catalogItemJS, catItemDetails)) {
                var response = this._responseObjectFormatter(catItemDetails, catItemDetails.sys_class_name, count.getAggregate('COUNT''catalog_item'));
                var massageWith = this._getMassageObject(catItemDetails.name, count.getAggregate('COUNT''catalog_item'));
                itemsResult.push(this._massageResponseObject(response, massageWith));
                citm++;
            }
        }
        var guides = 0;
        count = new GlideAggregate('catalog_channel_analytics');
        count.setCategory('catalog_meta');
        count.setGroupByFollowRef(false);
        count.setOrderByFollowRef(false);
        count.addAggregate('COUNT''sys_id');
        count.groupBy('catalog_item');
        count.addNotNullQuery('catalog_item');
        count.addQuery('table_name''sc_cat_item_guide');
        count.addEncodedQuery(this._basicQueryFilters[this._baseQuery] || '');
        if (Array.isArray(this._allowedItemsList))
            count.addQuery('catalog_item'"IN"this._allowedItemsList);
        count.orderByAggregate('COUNT''sys_id');
        count.query();
        while (guides < this._itemsLimit && count.next()) {
            var guideId = count.getValue("catalog_item");
            catalogItemJS = new sn_sc.CatItem(guideId);
            catItemDetails = this._itemSummaryFetcher(catalogItemJS);
            if (!this._allowItem(catalogItemJS, catItemDetails))
                continue;
            if (this._itemValidator(catalogItemJS, catItemDetails)) {
                response = this._responseObjectFormatter(catItemDetails, 'sc_cat_item_guide', count.getAggregate('COUNT''sys_id'));
                massageWith = this._getMassageObject(catItemDetails.name, count.getAggregate('COUNT''sys_id'));
                itemsResult.push(this._massageResponseObject(response, massageWith));
                guides++;
            }
        }
        itemsResult.sort(function(a, b) {
            return a.order == b.order ? a.name.localeCompare(b.name) : -(a.order - b.order);
        });
        return (itemsResult.length > this._itemsLimit) ?
            itemsResult.slice(itemsResult.length - this._itemsLimit, itemsResult.length) :
            itemsResult;
    },

    type: 'MySCPopularItems'
});

 

1 ACCEPTED SOLUTION

Sorry for the delay in responding, but I'm glad my previous response triggered the solution, as it was exactly what I was going for 🙂   I hate simple "do this" "answers".

 

When we create a Script Include that "overrides" another one, it doesn't automagically override it.  We must use the new class in order for the overrides to apply.  That is what I figured was the issue you were having as you did not mention using the new class name in your code.

 

We actually did this for a client with the OOTB Script Include used to create new User records because we wanted to use our own set of temporary bilingual passwords instead of the set used by SN.  We created the new Script Include and pointed a couple of lines of code to use it so that the new passwords would be used but not change the rest of the functionality of the Script Include.

 

It's kind of hard to decide which method is "best": override/clone an OOTB record or modify it.  I struggle with that all the time.  Overriding or cloning something helps on upgrades because you do not have to review skipped records and you'll get any updated code automatically.  BUT, at least the skipped record is a trigger for you to look at something that has potentially changed.  Maybe you don't actually want the new code to be used because your code is important.  Maybe you changed something and the newest SN code actually works the way you want it to now but you'd never know it unless you actively look at the code that you've cloned or overridden (which is not very likely).  This gives you a means to revert back to the OOTB code.  You can't compare code from 2 different records in SN directly, so you'd have to do it manually.

 

Another thing, like you mentioned, is some new code may have been introduced that uses the "old" OOTB code that you would prefer use the new Script Include you created.  You'd have to actively search for those on upgrades to be sure your new code is used.

 

I know someone will jump in and say "best practice" says you never touch OOTB code.  Well, I prefer the term "good practice", as the "best" way to do things is not the best way for everything/everyone/all the time.  There is very rarely an absolute answer to everything.

 

Please remember to mark any response as being the correct one or helpful to you in solving the problem.

View solution in original post

21 REPLIES 21

Community Alums
Not applicable

Hi @sarah_kovar ,

Have you tried this approach ?

var MySCPopularItems = new (API Name of script Include);

Hi, 

That approach doesn't work. 

Thanks.

Bert_c1
Kilo Patron

Hi Sarah,

 

Unless your script include is defined in the 'Global' application scope, gs.log() won't work. Instead use 'gs.info();' that does work in non-global application scopes.  As as seen in the OOB examples, an 'initialize' function is needed. The following is from the 'Incident' script include that can be used to override functions in the 'IncidentSNC' script include.

 

var Incident = Class.create();
Incident.prototype = Object.extendsObject(IncidentSNC, {

	initialize: function(incidentGr) {
		IncidentSNC.prototype.initialize.call(this, incidentGr);
	},
	
    type: 'Incident'
});

 

I hope you find success.

Hi, thanks. They're both in global. My script include is copied from an OOB example and as stated in my original post I have tried with the initialize function and without. 

Bert_c1
Kilo Patron

Hi Sarah,

 

My script include:

 

 

var Incident = Class.create();
Incident.prototype = Object.extendsObject(IncidentSNC, {

	initialize: function(incidentGr) {
		IncidentSNC.prototype.initialize.call(this, incidentGr);
	},

	logMessage: function(msg) {
		gs.log('My logMessage function has been called, msg: ' + msg);
	},

    type: 'Incident'
});

 

My test script (run in Scripts Background):

 

var inc = new GlideRecord('incident');
inc.query();
if (inc.next()) {
var incObj = new Incident();
incObj.logMessage("Number is " + inc.number);
incObj.logMessage("This is msg2.");
}

 

Output from scripts background:

 

*** Script: My logMessage function has been called, msg: Number is INC0000060
*** Script: My logMessage function has been called, msg: This is msg2.

 

From Script Log Statements:

Screenshot 2023-07-10 112117.png

 

Good luck on your implementation.