UNPKG

h54s

Version:

HTML5 Data Adapter for SAS

191 lines (164 loc) 7.65 kB
const h54sError = require('./error.js'); const sasVersionMap = { v9: { url: '/SASStoredProcess/do', loginUrl: '/SASLogon/login', logoutUrl: '/SASStoredProcess/do?_action=logoff', RESTAuthLoginUrl: '/SASLogon/v1/tickets' }, viya: { url: '/SASJobExecution/', loginUrl: '/SASLogon/login.do', logoutUrl: '/SASLogon/logout.do?', RESTAuthLoginUrl: '' } } /** * * @constructor * @param {Object} config - Configuration object for the H54S SAS Adapter * @param {String} config.sasVersion - Version of SAS, either 'v9' or 'viya' * @param {Boolean} config.debug - Whether debug mode is enabled, sets _debug=131 * @param {String} config.metadataRoot - Base path of all project services to be prepended to _program path * @param {String} config.url - URI of the job executor - SPWA or JES * @param {String} config.loginUrl - URI of the SASLogon web login path - overridden by form action * @param {String} config.logoutUrl - URI of the logout action * @param {String} config.RESTauth - Boolean to toggle use of REST authentication in SAS v9 * @param {String} config.RESTauthLoginUrl - Address of SASLogon tickets endpoint for REST auth * @param {Boolean} config.retryAfterLogin - Whether to resume requests which were parked with login redirect after a successful re-login * @param {Number} config.maxXhrRetries - If a program call fails, attempt to call it again N times until it succeeds * @param {Number} config.ajaxTimeout - Number of milliseconds to wait for a response before closing the request * @param {Boolean} config.useMultipartFormData - Whether to use multipart for POST - for legacy backend support * @param {String} config.csrf - CSRF token for JES * @ * */ const h54s = module.exports = function(config) { // Default config values, overridden by anything in the config object this.sasVersion = (config && config.sasVersion) || 'v9' //use v9 as default= this.debug = (config && config.debug) || false; this.metadataRoot = (config && config.metadataRoot) || ''; this.url = sasVersionMap[this.sasVersion].url; this.loginUrl = sasVersionMap[this.sasVersion].loginUrl; this.logoutUrl = sasVersionMap[this.sasVersion].logoutUrl; this.RESTauth = false; this.RESTauthLoginUrl = sasVersionMap[this.sasVersion].RESTAuthLoginUrl; this.retryAfterLogin = true; this.maxXhrRetries = 5; this.ajaxTimeout = (config && config.ajaxTimeout) || 300000; this.useMultipartFormData = (config && config.useMultipartFormData) || true; this.csrf = '' this.isViya = this.sasVersion === 'viya'; // Initialising callback stacks for when authentication is paused this.remoteConfigUpdateCallbacks = []; this._pendingCalls = []; this._customPendingCalls = []; this._disableCalls = false this._ajax = require('./methods/ajax.js')(); _setConfig.call(this, config); // If this instance was deployed with a standalone config external to the build use that if(config && config.isRemoteConfig) { const self = this; this._disableCalls = true; // 'h54sConfig.json' is for the testing with karma //replaced by gulp in dev build (defined in gulpfile under proxies) this._ajax.get('h54sConfig.json').success(function(res) { const remoteConfig = JSON.parse(res.responseText) // Save local config before updating it with remote config const localConfig = Object.assign({}, config) const oldMetadataRoot = localConfig.metadataRoot; for(let key in remoteConfig) { if(remoteConfig.hasOwnProperty(key) && key !== 'isRemoteConfig') { config[key] = remoteConfig[key]; } } _setConfig.call(self, config); // Execute callbacks when overrides from remote config are applied for(let i = 0, n = self.remoteConfigUpdateCallbacks.length; i < n; i++) { const fn = self.remoteConfigUpdateCallbacks[i]; fn(); } // Execute sas calls disabled while waiting for the config self._disableCalls = false; while(self._pendingCalls.length > 0) { const pendingCall = self._pendingCalls.shift(); const sasProgram = pendingCall.options.sasProgram; const callbackPending = pendingCall.options.callback; const params = pendingCall.params; //update debug because it may change in the meantime params._debug = self.debug ? 131 : 0; // Update program path with metadataRoot if it's not set if(self.metadataRoot && params._program.indexOf(self.metadataRoot) === -1) { params._program = self.metadataRoot.replace(/\/?$/, '/') + params._program.replace(oldMetadataRoot, '').replace(/^\//, ''); } // Update debug because it may change in the meantime params._debug = self.debug ? 131 : 0; self.call(sasProgram, null, callbackPending, params); } // Execute custom calls that we made while waitinf for the config while(self._customPendingCalls.length > 0) { const pendingCall = self._customPendingCalls.shift() const callMethod = pendingCall.callMethod const _url = pendingCall._url const options = pendingCall.options; ///update program with metadataRoot if it's not set if(self.metadataRoot && options.params && options.params._program.indexOf(self.metadataRoot) === -1) { options.params._program = self.metadataRoot.replace(/\/?$/, '/') + options.params._program.replace(oldMetadataRoot, '').replace(/^\//, ''); } //update debug because it also may have changed from remoteConfig if (options.params) { options.params._debug = self.debug ? 131 : 0; } self.managedRequest(callMethod, _url, options); } }).error(function (err) { throw new h54sError('ajaxError', 'Remote config file cannot be loaded. Http status code: ' + err.status); }); } // private function to set h54s instance properties function _setConfig(config) { if(!config) { this._ajax.setTimeout(this.ajaxTimeout); return; } else if(typeof config !== 'object') { throw new h54sError('argumentError', 'First parameter should be config object'); } //merge config object from parameter with this for(let key in config) { if(config.hasOwnProperty(key)) { if((key === 'url' || key === 'loginUrl') && config[key].charAt(0) !== '/') { config[key] = '/' + config[key]; } this[key] = config[key]; } } //if server is remote use the full server url //NOTE: This requires CORS and is here for legacy support if(config.hostUrl) { if(config.hostUrl.charAt(config.hostUrl.length - 1) === '/') { config.hostUrl = config.hostUrl.slice(0, -1); } this.hostUrl = config.hostUrl; if (!this.url.includes(this.hostUrl)) { this.url = config.hostUrl + this.url; } if (!this.loginUrl.includes(this.hostUrl)) { this.loginUrl = config.hostUrl + this.loginUrl; } if (!this.RESTauthLoginUrl.includes(this.hostUrl)) { this.RESTauthLoginUrl = config.hostUrl + this.RESTauthLoginUrl; } } this._ajax.setTimeout(this.ajaxTimeout); } }; // replaced by gulp with real version at build time h54s.version = '__version__'; h54s.prototype = require('./methods'); h54s.Tables = require('./tables'); h54s.Files = require('./files'); h54s.SasData = require('./sasData.js'); h54s.fromSasDateTime = require('./methods/utils.js').fromSasDateTime; h54s.toSasDateTime = require('./tables/utils.js').toSasDateTime; //self invoked function module require('./ie_polyfills.js');