script include

Standardisera återanvändning av JavaScript i ServiceNow

Det blir normalt sett en del scriptande i ServiceNow. Smart grej är att tänkta på att designa koden så den går att återanvända. Då slipper man själv, och kollegorna, att utveckla samma funktion flera gånger och man spar tid. Man måste så klart ha ett arbetssätt så kollegorna vet om vad det finns för funktion att tillgå utan att man måste hålla på med för mycket dokumentering. Har man ett standardiserat sätt att jobba med återanvändning så ska det vara lätt att hitta funktion som har blivit utvecklad i systemet.

Allt i denna artikel är gjort i Orlando.

Naturliga ställen för att skapa kod för återanvändning är i Script include på serversidan och UI Script på klientsidan. Vi skapar klasser i vilka vi kan ha funktioner som vi når från övriga script på server- respektive klientsidan. Det är praktiskt om vi kan använda oss av abstrakta metoder så vi slipper skapa objekt innan användning. Det kan gå, men inte alltid. Exempelvis måste vi skapa objekt om vi vill att objektet ska ha kunna hålla på värden mellan anrop till klassens funktioner.

Det var ju enkelt, då gör vi så. Var det allt? Nja, vi kanske måste kolla på hur vi dokumenterar detta. Bra kod dokumenterar sig själv säger man och man räknar med kommentarer i texten till det så är det helt sant. Helst skulle man ju vilja generera JSDoc från koden man gjort men det tittar vi inte på nu. Sen måste vi titta lite på hur vi skapar klasserna också. Vill vi ha abstrakta metoder? Inte lika rättframt som för Java även om det är besläktade språk.

Namnstandard

För att vara säkra på att de klasser vi skapar inte kolliderar med de klasser som redan finns i ServiceNow behöver vi en namnstandard.

<prefix><objekt>Utils

Prefix är 1-2 bokstäver som gör att klassen skiljer sig om vi råka skapa en klass för ett objekt där ServiceNow redan har skapat en klass.

Objekt är den, troligtvis, tabell som ska användas. Kan vara andra entiteter också.

Utils är bara en statisk text som visar att det är någon form av verktygs- eller hjälpklass.

Exempel:

scIncidentUtils

‘sc’ står i det här fallet för SMICloud (mitt företag). ‘incident’ för att klassen ska användas i incidentsammanhang. Hade vi inte lagt till ‘sc’ i början hade vi fått krock med incidentUtils, på serversidan, som kommer med ServiceNow.

Klass

Vi skapar klasser i UI Script för klientsidan och Script Includes för serversidan. Vi jobbar med klasser för det är välstrukturerat sätt att kapsla in funktioner relaterade till samma område. Vi kommer kunna anropa en funktion genom att antingen anropa den direkt via klassen (abstract) eller skapa ett objekt och använda det för att anropa funktioner. Här kommer du behöva ta ett beslut, behöver du att klassen bär på data som du ska använda senare i scriptet måste du skapa ett objekt. Behöver du använda en funktion bara för att göra ett anrop och sen behöver du inte klassen längre, du kan du anropa funktionen utan att skapa ett objekt. Kallas abstrakt funktion normalt sett (oklart om det är rätt term i det här fallet, men sättet är iaf likt).

Exempel

Låt säga att en incident inte ska kunna relatera till ett problem som har lägre prioritet än själva incidenten. Vi gör jämförelsen när vi försöker spara när incidenten fått ett nytt relaterat problem eller när incidentens prioritet ändras. Är problemets prioritet lägre än incidentens så ska inte uppdateringen sparas och ett meddelande skrivas i formuläret. I en komplett lösning måste man även implementera kontroller på problemsidan men vi utgår bara från incidentsidan i detta exempel.

Vi skapar först ett Script include som göra själva jämförelsen (Jag har inte använt mig av fellhanteringen jag pratar om här för att göra koden så ren som möjligt. Lägg gärna till den i skarpa fall.):

/** @class      scIncidentUtil      Utility class incidents */
var scIncidentUtil = Class.create();
scIncidentUtil.prototype = Object.extendsObject(AbstractAjaxProcessor, {

    /** Compare two objects' priorities. The both input parameters/objects must to have a field 'priority' of the same format. 
    *@param     reference1      The first reference object that the priority should be compared with.  
    *                       reference2      The second reference object that the priority should be compared with. 
    **/
    comparePriorities: function(reference1, reference2) {
            //Compare using normal javascript functionality and return. Exactly what should happen is not decided here since that would minimize the general use of this function
            return reference1.priority.localeCompare(reference2.priority);

    },

    type: 'scIncidentUtil'
});

Notera att denna Script include är det API vi skapat och i vilken vi ska fylla på med funktioner relaterat till incidenter. Skapa en annan Script include för övriga tabeller/objekt.

Vi skapar sen en Business rule från vilken vi använder vår util-klass och funktion. Sätt följande värden:

  • Markera Advanced då vi måste scripta.

  • When sätts till before (om vi måste göra ‘abort’ så får inget sparas innan.

  • Business rule används när vi skapar och uppdaterar incidenten så markera Insert och Update.

  • Kör regeln endast om man väljer nytt relaterat problem till incidenten eller när incidentens prioritet uppdateras. Sätts i Filter Condition.

Business rule

Business rule


Och så klipper vi in detta scrip under flik Advanced:

(function executeRule(current, previous /*null when async*/) {

    //In case problem has a lower priority than the incident, abort and inform the user.
    var comparison = scIncidentUtil.prototype.comparePriorities(current, current.problem_id);
    if(comparison == -1){
        gs.addErrorMessage("Incident cannot have higher priority than related problem. Update not saved.");
    }
    
})(current, previous);

När vi nu uppdaterar incidentens prioritet eller relaterade problem görs kontrollen. Om incidentens prioritet är lägre eller lika med problemets prioritet så händer inget. Annars skrivs ett meddelande ut i formuläret:

Felmeddelande i incidentformuläret

Felmeddelande i incidentformuläret

Ett alternativ till ovan lösning hade kunna vara att lägga den på task-nivå. Då hade man kunnat använda den för objekt som ärver task-tabellen också. I detta scenario så sker jämförelsen på ett lite annat sätt på problemsidan så kanske är det bra att skapa en utility-klass för problem där man itererar igenom alla relaterade incidenter och gör en jämförelse. Ingenting hindrar dig från att göra själva iterationen i en utility-klass för problem och göra de enskilda jämförelserna m.h.a. denna utility-klass för incident (även om det kanske är lite overkill för en sådan enkel jämförelse).

Fortsätt läsa