ServiceNow’s OOB utmaningar kring att ta emot strömmat data i stora mängder (läs response paket större än 32 Mb för datatypen String). För att gå runt utmaningen har jag löst det genom att spara svaret oströmmat som ett attachment (datatyp: String)


var r = new sn_ws.RESTMessageV2("xxxx", "yyyy");
r.setBasicAuth(ssss, rrrr);
var response = r.execute();
response.waitForResponse(200);
//Write the huge attachment from response body
var attachSysId = this.attachment.write(<GlideRecord>, <filnamn>, 'plain/text', response.getBody());

Nu har vi svaret som en bifogad fil så nu behöver vi göra det lilla “magiska” och strömma tillbaka svaret i mindre portioner.


var arrayNumber = this.getStreamAndParseArray(attachSysId);

getStreamAndParseArray: function(attachSysId) {
var inputStream = this.attachment.getContentStream(attachSysId);
var reader = new GlideTextReader(inputStream);
var ln = ' ';
this["responseArray1"] = [];
var arrayNumber =  1;
var threadhold = 15000;
while ((ln = reader.readLine()) != null) {
    if (this.loopIndex == (threadhold * arrayNumber)) {
	/*If the response from XXXX are to big to handle the normal ServiceNow way we need to split the huge attachment into smaller chunks. In this case I have choosen the number of 15000 records per String Array*/
	arrayNumber++;
	this["responseArray"+arrayNumber] = []; 
    }
    this["responseArray"+arrayNumber].push(ln);
    this.loopIndex++;
    }
    return arrayNumber;
}

Efter jobbet att gå igenom hela strömmen uppdelat på xx antal array (responseArray) så ska jag ladda en Datakälla (data source) som sedan gör själva jobbet och det går med speed lightning. Lösningen jag valde här är att skapa en CSV fil som är enkel och mycket snabb, obefintlig “overhead” som ex finns för både SOAP och REST protokollen. Varje Array blir en fil som sparas i vald datakälla som då laddas och transformeras osv tills alla Array är klara.


doLoadAndTransform: function(arrayNumber, dataSource, filename, attachSysId, firstTransformName) {
//Parse and create a new CSV file, more easy for ServiceNow to handle
for (var parsedArrayCount = 1; parsedArrayCount <= arrayNumber; parsedArrayCount++) {
	gs.log('XXXX parsed # array: ' + this["responseArray" + parsedArrayCount].length);
	var stringJson = '{"import": [' + this["responseArray" + parsedArrayCount] + ']}';
	var parsedJson = JSON.parse(stringJson);
	var csv = this.createCSVObject(parsedJson['import']);
	//Attach the newly created CSV file to our Data Source
	attachSysId = this.attachment.write(dataSource, filename, 'plain/text; charset=utf-8', csv);
	//Initiate DataSourceLoader and call the load function to do the actual load and tranforms
	var ret = this.dt.load(dataSource.getValue('sys_id'), firstTransformName, false);
	gs.log('XXXX DataSource loaded for array #' + parsedArrayCount + ' of total #' + arrayNumber);
	//Delete the current Array file/attachment from the Data Source record
	gs.log('XXXX try to delete loaded attachment');
	this.deleteAttachments(filename, dataSource.getValue('sys_id'));
	}
}

Ett exempel att skapa en CSV variable från ett JSON


createCSVObject: function(json) {
  var fields = Object.keys(json[0]);
  var replacer = function (key, value) {
	return value === null ? '' : value;
  };
  var csv = json.map(function (row) {
	return fields.map(function (fieldName) {
	return JSON.stringify(row[fieldName], replacer);
  }).join(';');
  });
  csv.unshift(fields.join(';'));
  csv = csv.join('\r\n');
  return csv;
}

Frågor eller synpunkter, kommentera gärna!

Har du inte redan testat att skapa automatiska tester med “Automated Test Framework” (ATF) som kom med Istanbul (och med flera förbättringar i Jakarta) så gör det! Det är otroligt lätt att skapa automatiska tester med detta smidiga ramverk, och dessa kan sedan köras vid uppgraderingar eller vid andra förändringar i instansen för att hjälpa till att verifiera att allt fortfarande fungerar som det ska!

När man skapar automatiska tester så är ofta ett “Impersonate”-steg det första man gör. Genom att köra testet som en specifik användare så kan man säkerställa att t.ex. ACL:er är uppsatta korrekt. Ett problem dock, som jag stött på när jag skapat tester, är att hitta en bra användare att använda för testet. Ett exempel:

Säg att jag vill testa att användare som har rollen X får skapa records i en viss tabell, tabell T. I min instans finns tre användare med rollen X: användare A, B och C. Dessa användare har däremot fler roller än bara rollen X:

A har rollerna X och Y
B har rollerna X och Z
C har rollerna X, Y och Z

Vilken användare ska jag använda i mitt “Impersonate”-steg för att säkerställa att just rollen X ger rättighet att skapa records i tabellen T? Kommer testet fortfarande fungera om rollerna för den användare jag väljer skulle ändras?

Lösningen: För att slippa fundera över detta så kan man istället skapa en ny användare, “ATF Test User” t.ex., för användning vid skapandet av automatiska tester. “ATF Test User” har inga roller alls, utan tilldelas de roller som behövs vid exekvering av själva testet! För att testa om rollen X ger en rättighet att skapa records i tabellen T kan ett test med följande test-steg skapas:

Steg 1: Record Insert
Table: User Role [sys_user_has_role]
Field values: [User = ATF Test User] [Role = X]

Detta steget ser till att “ATF Test User”-användaren tilldelas rollen X.

Steg 2: Impersonate
User: ATF Test User (atf_test_user)

Alla test-steg efter detta kommer att exekveras som “ATF Test User” med rollen X.

Steg 3: Record Insert
Table: T [u_t]
Enforce security: true
Field values:

Går detta test-steget igenom så vet vi att rollen X ger rättighet att skapa records i tabellen T.

Måste man inte “städa bort” roll-tilldelningarna igen innan testet exekverats klart? Nix, test-ramverket gör automatiskt en “Rollback” på records som skapas med “Record Insert”! 🙂

Bonus-tips! Vill man testa flera olika roller i samma test så kan man använda “Record Delete” för att ta bort tidigare skapade records i sys_user_has_role-tabellen (använd referensen till det tidigare test-steget!) och sedan skapa ett nytt record för en ny roll. Se dock till att göra om “Impersonate”-steget efter detta, och gör då först “Impersonate” på en annan användare än “ATF Test User”-användaren, för att roll-tilldelningen ska slå.

 

Kan användas för att skapa ex. relaterade objekt

Skapa en UI action och ange minst följande:

(Insert/Update=checked,client=checked,Onclick=openModalForm(),form button=checked)

//Show modal with a view (form) for xxx table
function openModalForm() {
    var modalForm = new GlideModalForm('Title' /*Name the modal Title*/ , 'task' /*Table for the new record*/ );
    modalForm.setOnloadCallback(formOnLoadCallback);
    modalForm.setCompletionCallback(formAfterSubmitCallback);
    modalForm.render();
}

function formOnLoadCallback() {
    //Access GlideModal g_form to set field for the new record
    var d_form = window.frames["dialog_frame"].g_form;
    d_form.setValue('field', g_form.getValue('field'));
    d_form.setValue('field', g_form.getValue('field'));
}

function formAfterSubmitCallback(action_verb, sys_id, table, displayValue) {
    //Get the newly created record sys_id and then set e.g a value to the starting record
    g_form.setValue('field', sys_id);
    //Save the record
    g_form.save();
}
Testat för Jakarta p7