UNPKG

7.65 kBJavaScriptView Raw
1const h54sError = require('./error.js');
2
3const sasVersionMap = {
4 v9: {
5 url: '/SASStoredProcess/do',
6 loginUrl: '/SASLogon/login',
7 logoutUrl: '/SASStoredProcess/do?_action=logoff',
8 RESTAuthLoginUrl: '/SASLogon/v1/tickets'
9 },
10 viya: {
11 url: '/SASJobExecution/',
12 loginUrl: '/SASLogon/login.do',
13 logoutUrl: '/SASLogon/logout.do?',
14 RESTAuthLoginUrl: ''
15 }
16}
17
18/**
19*
20* @constructor
21* @param {Object} config - Configuration object for the H54S SAS Adapter
22* @param {String} config.sasVersion - Version of SAS, either 'v9' or 'viya'
23* @param {Boolean} config.debug - Whether debug mode is enabled, sets _debug=131
24* @param {String} config.metadataRoot - Base path of all project services to be prepended to _program path
25* @param {String} config.url - URI of the job executor - SPWA or JES
26* @param {String} config.loginUrl - URI of the SASLogon web login path - overridden by form action
27* @param {String} config.logoutUrl - URI of the logout action
28* @param {String} config.RESTauth - Boolean to toggle use of REST authentication in SAS v9
29* @param {String} config.RESTauthLoginUrl - Address of SASLogon tickets endpoint for REST auth
30* @param {Boolean} config.retryAfterLogin - Whether to resume requests which were parked with login redirect after a successful re-login
31* @param {Number} config.maxXhrRetries - If a program call fails, attempt to call it again N times until it succeeds
32* @param {Number} config.ajaxTimeout - Number of milliseconds to wait for a response before closing the request
33* @param {Boolean} config.useMultipartFormData - Whether to use multipart for POST - for legacy backend support
34* @param {String} config.csrf - CSRF token for JES
35* @
36*
37*/
38const h54s = module.exports = function(config) {
39 // Default config values, overridden by anything in the config object
40 this.sasVersion = (config && config.sasVersion) || 'v9' //use v9 as default=
41 this.debug = (config && config.debug) || false;
42 this.metadataRoot = (config && config.metadataRoot) || '';
43 this.url = sasVersionMap[this.sasVersion].url;
44 this.loginUrl = sasVersionMap[this.sasVersion].loginUrl;
45 this.logoutUrl = sasVersionMap[this.sasVersion].logoutUrl;
46 this.RESTauth = false;
47 this.RESTauthLoginUrl = sasVersionMap[this.sasVersion].RESTAuthLoginUrl;
48 this.retryAfterLogin = true;
49 this.maxXhrRetries = 5;
50 this.ajaxTimeout = (config && config.ajaxTimeout) || 300000;
51 this.useMultipartFormData = (config && config.useMultipartFormData) || true;
52 this.csrf = ''
53 this.isViya = this.sasVersion === 'viya';
54
55 // Initialising callback stacks for when authentication is paused
56 this.remoteConfigUpdateCallbacks = [];
57 this._pendingCalls = [];
58 this._customPendingCalls = [];
59 this._disableCalls = false
60 this._ajax = require('./methods/ajax.js')();
61
62 _setConfig.call(this, config);
63
64 // If this instance was deployed with a standalone config external to the build use that
65 if(config && config.isRemoteConfig) {
66 const self = this;
67
68 this._disableCalls = true;
69
70 // 'h54sConfig.json' is for the testing with karma
71 //replaced by gulp in dev build (defined in gulpfile under proxies)
72 this._ajax.get('h54sConfig.json').success(function(res) {
73 const remoteConfig = JSON.parse(res.responseText)
74
75 // Save local config before updating it with remote config
76 const localConfig = Object.assign({}, config)
77 const oldMetadataRoot = localConfig.metadataRoot;
78
79 for(let key in remoteConfig) {
80 if(remoteConfig.hasOwnProperty(key) && key !== 'isRemoteConfig') {
81 config[key] = remoteConfig[key];
82 }
83 }
84
85 _setConfig.call(self, config);
86
87 // Execute callbacks when overrides from remote config are applied
88 for(let i = 0, n = self.remoteConfigUpdateCallbacks.length; i < n; i++) {
89 const fn = self.remoteConfigUpdateCallbacks[i];
90 fn();
91 }
92
93 // Execute sas calls disabled while waiting for the config
94 self._disableCalls = false;
95 while(self._pendingCalls.length > 0) {
96 const pendingCall = self._pendingCalls.shift();
97 const sasProgram = pendingCall.options.sasProgram;
98 const callbackPending = pendingCall.options.callback;
99 const params = pendingCall.params;
100 //update debug because it may change in the meantime
101 params._debug = self.debug ? 131 : 0;
102
103 // Update program path with metadataRoot if it's not set
104 if(self.metadataRoot && params._program.indexOf(self.metadataRoot) === -1) {
105 params._program = self.metadataRoot.replace(/\/?$/, '/') + params._program.replace(oldMetadataRoot, '').replace(/^\//, '');
106 }
107
108 // Update debug because it may change in the meantime
109 params._debug = self.debug ? 131 : 0;
110
111 self.call(sasProgram, null, callbackPending, params);
112 }
113
114 // Execute custom calls that we made while waitinf for the config
115 while(self._customPendingCalls.length > 0) {
116 const pendingCall = self._customPendingCalls.shift()
117 const callMethod = pendingCall.callMethod
118 const _url = pendingCall._url
119 const options = pendingCall.options;
120 ///update program with metadataRoot if it's not set
121 if(self.metadataRoot && options.params && options.params._program.indexOf(self.metadataRoot) === -1) {
122 options.params._program = self.metadataRoot.replace(/\/?$/, '/') + options.params._program.replace(oldMetadataRoot, '').replace(/^\//, '');
123 }
124 //update debug because it also may have changed from remoteConfig
125 if (options.params) {
126 options.params._debug = self.debug ? 131 : 0;
127 }
128 self.managedRequest(callMethod, _url, options);
129 }
130 }).error(function (err) {
131 throw new h54sError('ajaxError', 'Remote config file cannot be loaded. Http status code: ' + err.status);
132 });
133 }
134
135 // private function to set h54s instance properties
136 function _setConfig(config) {
137 if(!config) {
138 this._ajax.setTimeout(this.ajaxTimeout);
139 return;
140 } else if(typeof config !== 'object') {
141 throw new h54sError('argumentError', 'First parameter should be config object');
142 }
143
144 //merge config object from parameter with this
145 for(let key in config) {
146 if(config.hasOwnProperty(key)) {
147 if((key === 'url' || key === 'loginUrl') && config[key].charAt(0) !== '/') {
148 config[key] = '/' + config[key];
149 }
150 this[key] = config[key];
151 }
152 }
153
154 //if server is remote use the full server url
155 //NOTE: This requires CORS and is here for legacy support
156 if(config.hostUrl) {
157 if(config.hostUrl.charAt(config.hostUrl.length - 1) === '/') {
158 config.hostUrl = config.hostUrl.slice(0, -1);
159 }
160 this.hostUrl = config.hostUrl;
161 if (!this.url.includes(this.hostUrl)) {
162 this.url = config.hostUrl + this.url;
163 }
164 if (!this.loginUrl.includes(this.hostUrl)) {
165 this.loginUrl = config.hostUrl + this.loginUrl;
166 }
167 if (!this.RESTauthLoginUrl.includes(this.hostUrl)) {
168 this.RESTauthLoginUrl = config.hostUrl + this.RESTauthLoginUrl;
169 }
170 }
171
172 this._ajax.setTimeout(this.ajaxTimeout);
173 }
174};
175
176// replaced by gulp with real version at build time
177h54s.version = '__version__';
178
179
180h54s.prototype = require('./methods');
181
182h54s.Tables = require('./tables');
183h54s.Files = require('./files');
184h54s.SasData = require('./sasData.js');
185
186h54s.fromSasDateTime = require('./methods/utils.js').fromSasDateTime;
187h54s.toSasDateTime = require('./tables/utils.js').toSasDateTime;
188
189//self invoked function module
190require('./ie_polyfills.js');