Skip to content

Instantly share code, notes, and snippets.

@NielsGregers
Last active March 6, 2017 15:38
Show Gist options
  • Save NielsGregers/d725e83f602adf33b14d5b4301d2eca6 to your computer and use it in GitHub Desktop.
Save NielsGregers/d725e83f602adf33b14d5b4301d2eca6 to your computer and use it in GitHub Desktop.
ServiceNow (SN) and Visual Studio Team Services (VSTS) integration
/*
* Incidents from ServiceNow
*
*/
/**
* compares source and target array - match source.key with target.foreignKey
*
* @param {any} source
* @param {any} target
* @param {any} cb
* @returns
*/
function compareArray(source, target, cb) {
target = target.result;
var results = {
new: [],
removed: []
};
for (var x = 0; x < source.length; x++) {
var sourceItem = source[x];
var match = false;
for (var y = 0; y < target.length; y++) {
var targetItem = target[y];
if (targetItem.foreignKey === sourceItem.key) {
match = true;
}
}
if (!match) {
results.new.push(sourceItem);
}
}
for (var y = 0; y < target.length; y++) {
var targetItem = target[y];
var match = false;
for (var x = 0; x < source.length; x++) {
var sourceItem = source[x];
if (targetItem.foreignKey === sourceItem.key) {
match = true;
}
}
if (!match) {
results.removed.push(targetItem);
}
}
return cb(null, results);
}
/**
* reads Visual Studio WorkItems
*
* @param {any} auth
* @param {any} filter
* @param {any} cb
* @returns
*/
function readVisualStudioWorkItems(auth, tenant, project, filter, cb) {
var unirest = require("unirest");
var result = {
items: []
};
function check(items) {
try {
if (items.length <= 0) {
console.log("no more to process");
return cb(null, {
result: result.items
});
}
console.log("Remaining", items.length);
var comma = "";
var ids = "";
var count = 0;
while ((items.length > 0) && (count < 200)) {
var item = items.splice(0, 1);
ids += comma;
ids += item[0].id;
comma = ",";
}
var req = unirest("GET", "https://" + tenant + ".visualstudio.com/DefaultCollection/_apis/wit/workitems?ids=" + ids);
req.headers({
"cache-control": "no-cache",
"authorization": auth,
"content-type": "application/json",
"accept": "application/json"
});
req.end(function(res) {
if (res.error) {
return cb(res.error, null);
}
for (var x = 0; x < res.body.count; x++) {
var w = res.body.value[x];
var item = {};
item.title = w.fields["System.Title"];
var sntagIndex = item.title.indexOf("[SN:");
var suffix = item.title.substr(sntagIndex + 4);
var endtagIndex = suffix.indexOf("]");
if (endtagIndex > -1) {
item.serviceNowId = suffix.substr(0, endtagIndex);
item.foreignKey = "SN:" + item.serviceNowId;
}
result.items.push(item);
}
check(items); // take the next batch ..
});
} catch (error) {
return cb(error);
}
}
var req = unirest("POST", "https://" + tenant + ".visualstudio.com/DefaultCollection/_apis/wit/wiql");
req.query({
"api-version": "1.0"
});
req.headers({
"cache-control": "no-cache",
"authorization": auth,
"content-type": "application/json",
"accept": "application/json"
});
req.send("{query:' Select [System.Id], [System.Title], [System.State] From WorkItems Where [System.Title] CONTAINS \"[SN:\" AND [System.TeamProject] = \"" + project + "\" order by [Microsoft.VSTS.Common.Priority] asc, [System.CreatedDate] desc' }");
req.end(function(res) {
if (res.error) {
return cb(res.error, res.body);
}
check(res.body.workItems);
});
}
/**
* Reads ServiceNow incidents matching filter
*
* @param {any} auth
* @param {any} filter
* @param {any} cb
*/
function readServiceNowIncidents(auth, tenant, filter, cb) {
var unirest = require("unirest");
var req = unirest.get("https://" + tenant + ".service-now.com/api/now/v1/table/incident");
req.headers({
"cache-control": "no-cache",
"accept": "application/json",
"authorization": auth
});
req.query({
"sysparm_limit": "10000",
"sysparm_fields": "number,sys_id,u_incident_type,active,short_description,u_business_consequences,caller_id",
"sysparm_query": "active=true^" + filter,
"sysparm_view": "ess",
"sysparm_cancelable": "true"
});
req.end(function(res) {
if (res.error) {
return cb(res.error);
} else {
var results = [];
var callbacks = 0;
res.body.result.forEach(function(x) {
var item = {};
item.key = "SN:" + x.number;
item.number = x.number; //'INC0109501'
item.sys_id = x.sys_id; // 'e66acbb76fa7aa00e8cf2f7bcf3ee4b2'
item.title = x.short_description; //'Chrome is unable to communicate with google.com'
item.type = x.u_incident_type; // 'Incident'
item.status = x.active; // 'true'
item.body = x.u_business_consequences;
item.url = "https://" + tenant + ".service-now.com/nav_to.do?uri=vtb_task.do?sys_id=" + x.sys_id + "%26sysparm_view=ess";
callbacks += 1;
unirest
.get(x.caller_id.link)
.headers({
"cache-control": "no-cache",
"accept": "application/json",
"authorization": auth
})
.end(function(caller) {
callbacks -= 1;
if (caller.error) {
return cb(caller.error);
}
item.ownerDisplayName = caller.body.result.u_preferred_name;
item.ownerEmail = caller.body.result.email;
results.push(item);
if (callbacks === 0) {
return cb(null, results);
}
});
}, this);
}
});
}
/**
* emit new Items by creating Stamplay objects
*
* @param {any} stamplayObject
* @param {any} newItems
* @param {any} cb
* @returns
*/
function signalNew(auth, tenant, stamplayObject, newItems, cb) {
var Stamplay = require('stamplay');
var stamplay = new Stamplay(tenant, auth);
var jobs = 0;
var createdItems = [];
if (newItems.length === 0) {
return cb(null, createdItems);
}
newItems.forEach(function(newItem) {
jobs += 1;
var newObject = {
"key": newItem.key,
"sysid": newItem.sys_id,
"title": newItem.title,
"url": newItem.url,
"ownername": newItem.ownerDisplayName,
"owneremail": newItem.ownerEmail
};
stamplay.Object(stamplayObject)
.save(newObject,
function(err, res) {
if (err) {
return cb(err);
}
createdItems.push(res);
jobs -= 1;
if (jobs <= 0) {
return cb(null, createdItems);
}
});
}, this);
}
/**
* entry Point
*/
module.exports = function(context, cb) {
if (!context) { return cb("Need a context"); }
if (!context.secrets) { return cb("Need a context.secrets object"); }
if (!context.secrets.serviceNowAuth) { return cb("Need a context.secrets.serviceNowAuth property"); }
if (!context.secrets.visualStudioAuth) { return cb("Need a context.secrets.visualStudioAuth property"); }
if (!context.secrets.stamplayAuth) { return cb("Need a context.secrets.stamplayAuth property"); }
if (!context.body) { return cb("Need a context body object"); }
if (!context.body.serviceNowFilter) { return cb("Need a context.body.serviceNowFilter property"); }
if (!context.body.visualStudioFilter) { return cb("Need a context.body.serviceNowFilter property"); }
if (!context.body.stamPlayObject) { return cb("Need a context.body.stamPlayObject property"); }
if (!context.body.vstsProject) { return cb("Need a context.body.vstsProject"); }
if (!context.body.vstsTenant) { return cb("Need a context.body.vstsTenant"); }
if (!context.body.snTenant) { return cb("Need a context.body.snTenant"); }
if (!context.body.stamplayTenant) { return cb("Need a context.body.stamplayTenant"); }
readServiceNowIncidents(
context.secrets.serviceNowAuth,
context.body.snTenant,
context.body.serviceNowFilter,
function(err, incidents) {
if (err) {
return cb(err);
}
readVisualStudioWorkItems(
context.secrets.visualStudioAuth,
context.body.vstsTenant,
context.body.vstsProject,
context.body.visualStudioFilter,
function(err, items) {
if (err) {
return cb(err);
}
compareArray(
incidents,
items,
function(err, differences) {
if (err) {
return cb(err);
}
signalNew(
context.secrets.stamplayAuth,
context.body.stamplayTenant,
context.body.stamPlayObject,
differences.new,
function(err, newObjects) {
if (err) {
return cb(err);
}
return cb(null, newObjects);
});
});
});
});
};
module.exports.test = function(exec, secrets) {
exec({
body: {
serviceNowFilter: "cmdb_ci=e13650532b1e6a00cb7b393db5da15e5",
visualStudioFilter: "3N_CB",
stamPlayObject: "incident_for_3n_cb",
vstsProject: "3N_CB",
vstsTenant: secrets.tenant.vsto,
snTenant: secrets.tenant.snc,
stamplayTenant: secrets.tenant.stamplay,
},
secrets: {
serviceNowAuth: secrets.snc,
visualStudioAuth: secrets.vsto,
stamplayAuth: secrets.stamplay
}
}, module.exports);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment