extFile

Someone asked me if I had ever set up ServiceNow to grab files from an HTTP Resource and set it up as an attachment on a ServiceNow record. I had done something similar a year ago, so I decided to resurrect some code and made a function out of it. This function could be utilized in a business rule, workflow script, or in a Script Include library.

It is a little rough, but I wanted to just present it as an example of how to get an external resource (such as a document) and how to save it as an attachment. It only works for Calgary releases and beyond, but could easily be made more backward compatible.

In the following code example, I also showed how to handle Basic Authentication, should the resource be protected with Basic Auth credentials.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
//
// Example of how this function could be called, using some fake resources
//
var g = new GlideRecord("incident");
g.get("68ce631ce8d20100f8e6928117c296a5");

var remoteFile = "https://john-james-andersen.com/images/Branding038s-1.jpg";
var domain = "www.john-james-andersen.com";
var port = "80";
var contentType = "image/jpeg";
var realm = "Members Area";
var user = "myUser";
var password = "myUserPassword";

attachRemoteFileToRecord(g, remoteFile, contentType, domain, port, realm, user, password);



//
// The function that does the work
//

function attachRemoteFileToRecord(gr, url, contentType, domain, port, realm, user, pass) {

  //This call only works for Calgary -- I could code this up better to work with berlin as well
  //but I am too lazy
  var StringUtil = GlideStringUtil;

  //Set up some critical Java Packages call from the standard Java JDK
  var httpclient = Packages.org.apache.commons.httpclient;
  var HttpClient = httpclient.HttpClient;
  var UsernamePasswordCredentials = httpclient.UsernamePasswordCredentials;
  var AuthScope = httpclient.auth.AuthScope;
  var GetMethod = httpclient.methods.GetMethod;
  var ArrayList = Packages.java.util.ArrayList;
  var AuthPolicy = Packages.org.apache.commons.httpclient.auth.AuthPolicy;
  var AuthPNames = Packages.org.apache.http.auth.params.AuthPNames;
  var NTCredentials = Packages.org.apache.commons.httpclient.NTCredentials;
  var HttpTransportProperties = Packages.org.apache.axis2.transport.http.HttpTransportProperties;

  //Instantiate the HTTP Client
  var client = new HttpClient();
  var credentials = new UsernamePasswordCredentials(user, pass);

  //Handle any authentication
  var authScope = new AuthScope(domain, port, "Members Area");
  client.getState().setCredentials(authScope, credentials);
  var authPrefs = new ArrayList(1);
  authPrefs.add(AuthPolicy.BASIC);
  client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);

  gs.log("Getting URL: " + url);
  var get = new GetMethod(url);

  gs.log("AuthScope Auth Scheme: " + authScope.getScheme());
  gs.log("AuthScope Auth Realm: " + authScope.getRealm());
  gs.log("AuthScope Auth Host: " + authScope.getHost());
  gs.log("AuthScope Auth Port: " + authScope.getPort());
  gs.log("Credentials: " + credentials.getUserName() + " -- " + credentials.getPassword());

  var status = client.executeMethod(get);

  gs.log("Status Line: " + get.getStatusLine());
  //The following commented out code can be used for troubleshooting
  //gs.log("Response Body: " + get.getResponseBodyAsString());

  gs.log("--------------Request Headers------------------");
  var reqHeaders = get.getRequestHEaders();
  for ( var i = 0; i < reqHeaders.length; i++) {
    gs.log(reqHeaders[i].getName() + " (REQ):" + reqHeaders[i].getValue());
  }

  gs.log("--------------Response Headers------------------");
  var headers = get.getResponseHeaders();
  for ( var i = 0; i < headers.length; i++) {
    gs.log(headers[i].getName() + " (RESP) :" + headers[i].getValue());
  }

  //Prepping the input stream and the Glide library for attachments (Calgary format)
  var is = get.getResponseBodyAsStream();
  var sa = new GlideSysAttachment();

  var filename = getFileNameFromUrl(url);
  gs.log("FILENAME: "+filename);
 
  //Takes the input stream from the HTTP call and stream it into the attachment record      
  sa.write(gr.sys_id, gr.sys_class_name, filename, contentType, is);

  //Release the HTTP connection
  get.releaseConnection();
}


function getFileNameFromUrl( url ){
  url = ""+url;
  //remove any anchors
  url = url.substring(0, (url.indexOf("#") == -1) ? url.length : url.indexOf("#"));
  //remove any url parameters
  url = url.substring(0, (url.indexOf("?") == -1) ? url.length : url.indexOf("?"));
  //remove domain and path portions
  url = url.substring(url.lastIndexOf("/") + 1, url.length);
  return url;
}

Once this example script is run, I can go to the incident table and browse to the record with the matching sys_id value. There I will find the file attached to my record:

ServiceNow_Service_Automation