Skip to content

Instantly share code, notes, and snippets.

@vetsin
Created April 22, 2021 18:08
Show Gist options
  • Save vetsin/d5022c21006b57f3ee4c79efdbecc1f1 to your computer and use it in GitHub Desktop.
Save vetsin/d5022c21006b57f3ee4c79efdbecc1f1 to your computer and use it in GitHub Desktop.
ServiceNow utlitiy class for making sane HTTP requests from scoped script
/*
Usage Examples and etc.:
Basic GET:
```
var request = new Request();
request.get('https://httpbin.org/get', function(hasError, response, body) {
gs.info(body);
});
```
Basic POST:
```
var options = {
body: 'x=y'
}
request.post('https://httpbin.org/post', options, function(hasError, response, body) {
gs.info(body);
});
```
Or like so:
```
var options = {
uri: 'https://httpbin.org/post',
form: {'x':'y'}
}
request.post( options, function(hasError, response, body) {
gs.info(body);
});
```
All options are... optional.
```
var options = {
// can use `uri` OR `url`
uri: 'https://httpbin.org/post',
// instead of uri one can specify `host` OR `hostname`
host: 'httpbin.org',
schema: 'http', // defaults to `https`, used only with host
path: '', // defaults to `/`, used only with host
// auth supports `profile` || `user` || `custom`
auth: {
'profile': { // same as setAuthenticationProfile(type, id)
'type': '...',
'profileId': '...'
}
// OR
'user': { // basic authentication
'user': 'username', 'pass': 'password'
}
// OR
'custom': { // just some custom type
custom: function(params, restMessage) {
// for example:
restMessage.setHeader('Auth: custom AAAAZZZZ')
}
}
}
// standard query
query: {
'key': 'value'
}
// `body` || `form` || `json` in that order.
body: 'raw body data',
form: { 'x':'y', 'key': 'value '}, // encoded to `x=y&key=value` with proper header
json: { 'key': 'value ' }, // sent as json with proper header
// ignores `Host` header if specified
headers: { 'header', 'value' }
}
```
*/
var Request = Class.create();
Request.prototype = {
initialize: function(debug) {
this.debug = debug || false;
},
_initParams: function(uri, options, callback) {
if(this.debug) gs.debug('_initParams(' + uri + ', ' + options + ', ' + callback + ')')
if (typeof options === 'function') {
callback = options
}
var params = {}
if (typeof options === 'object') {
params = options;
params.uri = uri;
} else if (typeof uri === 'string') {
params.uri = uri;
} else {
params = uri;
}
params.callback = callback || params.callback;
// calculate URI if it's not provided
params = this._setup_uri(params);
return params;
},
_setup_uri: function(params) {
// People use this property instead all the time, so support it
if(!params.uri && params.url) {
params.uri = params.url;
delete params.url;
}
if(!params.uri && (params.host || params.hostname)) {
var schema = params.schema || 'https'
var host = params.host || params.hostname;
var uri = '' + schema + '://' + host;
var path = params.path || '/'
uri = uri + path;
params.uri = uri;
}
return params;
},
get: function(uri, options, callback) {
var params = this._initParams(uri, options, callback);
params.method = 'GET';
return this._execute(params);
},
post: function(uri, options, callback) {
var params = this._initParams(uri, options, callback);
params.method = 'POST';
return this._execute(params);
},
put: function(uri, options, callback) {
var params = this._initParams(uri, options, callback);
params.method = 'PUT';
return this._execute(params);
},
del: function(uri, options, callback) {
var params = this._initParams(uri, options, callback);
params.method = 'DELETE';
return this._execute(params);
},
_execute: function(params) {
var req = this._prepareRequest(params);
try {
var response = req.execute();
if(this.debug) {
gs.debug('response.haveError: ' + response.haveError());
if(response.haveError()) {
gs.debug('response.errorMessage: ' + response.getErrorMessage());
}
gs.debug('response.code: ' + response.getStatusCode());
gs.debug('response.body: ' + response.getBody());
}
var hasError = response.haveError() || (response.getStatusCode() > 400);
if(params.callback) {
return params.callback(hasError, response, response.getBody());
}
} catch (e) {
if(params.callback) {
return params.callback(true, e, '');
}
}
return undefined;
},
_prepareRequest: function(params) {
var rm = new sn_ws.RESTMessageV2();
rm.setHttpMethod(params.method || 'GET');
rm.setEndpoint(params.uri);
if(this.debug) gs.debug('' + params.method + ' ' + params.uri);
if('auth' in params) {
var authobj = params.auth;
if ('profile' in authobj) {
rm.setAuthenticationProfile(authobj.profile.type, authobj.profile.profileId);
} else if ('user' in authobj) {
// assume we have a pass too...
rm.setBasicAuth(authobj.user, authobj.pass);
} else if ('custom' in authobj) {
// Cause AWS and stuff
authobj.custom(params, rm);
}
}
var query_params = [];
for (var q in params.query) {
rm.setQueryParameter(q, params.query[q]);
query_params.push([q, encodeURIComponent(params.query[q])].join('='));
}
if(this.debug) gs.debug('query_params: ' + JSON.stringify(query_params));
if(params.body) {
rm.setRequestBody(params.body);
} else if (params.form) {
if(this.debug) gs.debug('Processing FORM request');
rm.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
var form_params = [];
for(var p in params.form) {
form_params.push([p, encodeURIComponent(params.form[p])].join('='));
}
if(this.debug) gs.debug(form_params.join('&'));
rm.setRequestBody(form_params.join('&'));
} else if (params.json) {
if(this.debug) gs.debug('Processing json request');
rm.setRequestHeader('Content-Type', 'application/json');
rm.setRequestBody(JSON.stringify(params.json));
}
if(this.debug) gs.debug('Extra Headers: ' + JSON.stringify(params.headers, null, 2));
for (var h in params.headers) {
//rm.setRequestHeader(h, encodeURIComponent(params.headers[h]));
if(h === 'Host') continue; // don't really want this one...
rm.setRequestHeader(h, params.headers[h]);
}
return rm;
},
type: 'Request'
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment