@sap/async-xsjs 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/LICENSE +37 -0
- package/README.md +445 -0
- package/differences.md +162 -0
- package/docs/$.Application.html +262 -0
- package/docs/$.Session.html +674 -0
- package/docs/$.db.CallableStatement.html +2524 -0
- package/docs/$.db.Connection.html +511 -0
- package/docs/$.db.ParameterMetaData.html +805 -0
- package/docs/$.db.PreparedStatement.html +1796 -0
- package/docs/$.db.ResultSet.html +1308 -0
- package/docs/$.db.ResultSetMetaData.html +800 -0
- package/docs/$.db.SQLException.html +259 -0
- package/docs/$.db.html +773 -0
- package/docs/$.hdb.ColumnMetadata.html +438 -0
- package/docs/$.hdb.Connection.html +663 -0
- package/docs/$.hdb.ProcedureResult.html +280 -0
- package/docs/$.hdb.ResultSet.html +324 -0
- package/docs/$.hdb.ResultSetIterator.html +315 -0
- package/docs/$.hdb.ResultSetMetaData.html +259 -0
- package/docs/$.hdb.SQLException.html +259 -0
- package/docs/$.hdb.html +557 -0
- package/docs/$.html +471 -0
- package/docs/$.jobs.Job.html +783 -0
- package/docs/$.jobs.JobLog.html +380 -0
- package/docs/$.jobs.JobSchedules.html +852 -0
- package/docs/$.jobs.html +238 -0
- package/docs/$.net.Destination.html +304 -0
- package/docs/$.net.Mail.Part.html +510 -0
- package/docs/$.net.Mail.html +504 -0
- package/docs/$.net.SMTPConnection.html +347 -0
- package/docs/$.net.html +749 -0
- package/docs/$.net.http.Client.html +562 -0
- package/docs/$.net.http.Destination.html +237 -0
- package/docs/$.net.http.Request.html +567 -0
- package/docs/$.net.http.html +292 -0
- package/docs/$.security.AntiVirus.html +361 -0
- package/docs/$.security.Store.html +636 -0
- package/docs/$.security.crypto.html +414 -0
- package/docs/$.security.html +247 -0
- package/docs/$.security.x509.html +373 -0
- package/docs/$.text.analysis.Session.html +983 -0
- package/docs/$.text.analysis.html +242 -0
- package/docs/$.text.html +246 -0
- package/docs/$.text.mining.Session.html +2018 -0
- package/docs/$.text.mining.html +242 -0
- package/docs/$.trace.html +525 -0
- package/docs/$.util.SAXParser.html +955 -0
- package/docs/$.util.Zip.html +474 -0
- package/docs/$.util.codec.html +414 -0
- package/docs/$.util.compression.html +357 -0
- package/docs/$.util.html +325 -0
- package/docs/$.util.sql.html +290 -0
- package/docs/$.web.Body.html +333 -0
- package/docs/$.web.EntityList.html +296 -0
- package/docs/$.web.TupelList.html +496 -0
- package/docs/$.web.WebEntityRequest.html +393 -0
- package/docs/$.web.WebEntityResponse.html +392 -0
- package/docs/$.web.WebRequest.html +560 -0
- package/docs/$.web.WebResponse.html +609 -0
- package/docs/$.web.html +246 -0
- package/docs/Copyright-SAP.html +39 -0
- package/docs/Disclaimer-SAP.html +55 -0
- package/docs/index.html +232 -0
- package/docs/styles/jsdoc-default.css +382 -0
- package/lib/AppConfig.js +36 -0
- package/lib/AuditLogger.js +41 -0
- package/lib/cacert.js +26 -0
- package/lib/ctypes.js +153 -0
- package/lib/destinations/dest-provider.js +57 -0
- package/lib/index.js +235 -0
- package/lib/jobs/Action.js +40 -0
- package/lib/jobs/Job.js +100 -0
- package/lib/jobs/JobManager.js +150 -0
- package/lib/jobs/JobsRuntime.js +133 -0
- package/lib/jobs/SqlScriptJobRunner.js +36 -0
- package/lib/jobs/XsjsJobRunner.js +78 -0
- package/lib/jobs/index.js +11 -0
- package/lib/logging.js +16 -0
- package/lib/middleware.js +125 -0
- package/lib/odata/ODataService.js +125 -0
- package/lib/odata/index.js +7 -0
- package/lib/odata/service-factory.js +26 -0
- package/lib/passport-noauth.js +17 -0
- package/lib/routes.js +115 -0
- package/lib/runtime.js +740 -0
- package/lib/sandbox.js +40 -0
- package/lib/utils/XsJsFunctionRunner.js +57 -0
- package/lib/utils/XsJsLibFunctionRunner.js +57 -0
- package/lib/utils/buffer-utils.js +77 -0
- package/lib/utils/compression-utils.js +14 -0
- package/lib/utils/date-utils.js +104 -0
- package/lib/utils/errors/HttpError.js +20 -0
- package/lib/utils/errors/wrap-app-error.js +18 -0
- package/lib/utils/index.js +17 -0
- package/lib/utils/xs-function-runner.js +51 -0
- package/lib/utils/xs-types.js +21 -0
- package/lib/utils/xspath.js +36 -0
- package/lib/utils/xsstack.js +28 -0
- package/lib/views/error.html +28 -0
- package/lib/xsjs/Application.js +28 -0
- package/lib/xsjs/Locale.js +53 -0
- package/lib/xsjs/Session.js +31 -0
- package/lib/xsjs/constants.js +71 -0
- package/lib/xsjs/db/common/DbBase.js +85 -0
- package/lib/xsjs/db/common/DbOptions.js +163 -0
- package/lib/xsjs/db/common/arguments-validation.js +102 -0
- package/lib/xsjs/db/common/connection.js +12 -0
- package/lib/xsjs/db/common/enums.js +93 -0
- package/lib/xsjs/db/common/execute-batch.js +38 -0
- package/lib/xsjs/db/common/parse-time.js +139 -0
- package/lib/xsjs/db/dbapi/CallableStatement.js +192 -0
- package/lib/xsjs/db/dbapi/Connection.js +78 -0
- package/lib/xsjs/db/dbapi/DB.js +39 -0
- package/lib/xsjs/db/dbapi/ParameterMetaData.js +118 -0
- package/lib/xsjs/db/dbapi/PreparedStatement.js +78 -0
- package/lib/xsjs/db/dbapi/ResultSet.js +220 -0
- package/lib/xsjs/db/dbapi/ResultSetMetaData.js +116 -0
- package/lib/xsjs/db/dbapi/Statement.js +514 -0
- package/lib/xsjs/db/dbapi/conversions.js +113 -0
- package/lib/xsjs/db/dbapi/fetch-rows.js +32 -0
- package/lib/xsjs/db/hdbapi/Connection.js +525 -0
- package/lib/xsjs/db/hdbapi/HDB.js +32 -0
- package/lib/xsjs/db/hdbapi/ResultSetIterator.js +40 -0
- package/lib/xsjs/db/hdbapi/convert.js +77 -0
- package/lib/xsjs/db/hdbapi/table-string-parser.js +52 -0
- package/lib/xsjs/db/index.js +4 -0
- package/lib/xsjs/index.js +13 -0
- package/lib/xsjs/jobs/Job.js +228 -0
- package/lib/xsjs/jobs/Jobs.js +11 -0
- package/lib/xsjs/jobs/Logs.js +127 -0
- package/lib/xsjs/jobs/Schedule.js +110 -0
- package/lib/xsjs/jobs/Schedules.js +108 -0
- package/lib/xsjs/net/Destination.js +43 -0
- package/lib/xsjs/net/http/Client.js +220 -0
- package/lib/xsjs/net/http/HTTP.js +72 -0
- package/lib/xsjs/net/index.js +5 -0
- package/lib/xsjs/net/smtp/Mail.js +38 -0
- package/lib/xsjs/net/smtp/Part.js +30 -0
- package/lib/xsjs/net/smtp/SMTPConnection.js +39 -0
- package/lib/xsjs/net/smtp/index.js +18 -0
- package/lib/xsjs/net/smtp/nodemailer-util.js +77 -0
- package/lib/xsjs/require.js +39 -0
- package/lib/xsjs/security/AntiVirus.js +31 -0
- package/lib/xsjs/security/Store.js +119 -0
- package/lib/xsjs/security/crypto.js +23 -0
- package/lib/xsjs/security/index.js +5 -0
- package/lib/xsjs/security/x509.js +12 -0
- package/lib/xsjs/text/analysis/Session.js +128 -0
- package/lib/xsjs/text/index.js +30 -0
- package/lib/xsjs/text/mining/Session.js +82 -0
- package/lib/xsjs/trace/trace.js +41 -0
- package/lib/xsjs/util/SAXParser.js +174 -0
- package/lib/xsjs/util/Zip.js +220 -0
- package/lib/xsjs/util/codec.js +33 -0
- package/lib/xsjs/util/compression.js +24 -0
- package/lib/xsjs/util/index.js +22 -0
- package/lib/xsjs/web/BasicWebEntity.js +41 -0
- package/lib/xsjs/web/EntityList.js +11 -0
- package/lib/xsjs/web/TupelLists/CookiesTupelList.js +47 -0
- package/lib/xsjs/web/TupelLists/HeadersTupelList.js +55 -0
- package/lib/xsjs/web/TupelLists/ParametersTupelList.js +83 -0
- package/lib/xsjs/web/TupelLists/TupelListBase.js +45 -0
- package/lib/xsjs/web/WebBody.js +135 -0
- package/lib/xsjs/web/WebEntityRequest.js +40 -0
- package/lib/xsjs/web/WebEntityResponse.js +26 -0
- package/lib/xsjs/web/WebRequest.js +209 -0
- package/lib/xsjs/web/WebResponse.js +183 -0
- package/lib/xsjs/web/index.js +4 -0
- package/lib/xsjs/web/utils/HeadersParser.js +53 -0
- package/lib/xsjs/web/utils/HttpRequestParser.js +93 -0
- package/lib/xsjs/web/utils/MultipartParser.js +163 -0
- package/lib/xsjs/web/utils/MultipartResponseBuilder.js +73 -0
- package/lib/xsjs/web/utils/SetCookieParser.js +32 -0
- package/lib/xsjslib/TextBundleWrapper.js +46 -0
- package/lib/xsjslib/index.js +11 -0
- package/npm-shrinkwrap.json +11540 -0
- package/package.json +84 -0
package/lib/runtime.js
ADDED
|
@@ -0,0 +1,740 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var assert = require('assert');
|
|
4
|
+
var fs = require('fs');
|
|
5
|
+
var fsp = require('fs').promises;
|
|
6
|
+
var path = require('path');
|
|
7
|
+
var vm = require('vm');
|
|
8
|
+
var _ = require('lodash');
|
|
9
|
+
var jobs = require('./jobs');
|
|
10
|
+
var logger = require('./logging').logger;
|
|
11
|
+
var sandbox = require('./sandbox');
|
|
12
|
+
var xs = require('./xsjs');
|
|
13
|
+
var utils = require('./utils');
|
|
14
|
+
var util = require('util');
|
|
15
|
+
var VError = require('verror');
|
|
16
|
+
var AppConfig = require('./AppConfig');
|
|
17
|
+
var suppLibs = require('./xsjslib');
|
|
18
|
+
var destProvider = require('./destinations/dest-provider');
|
|
19
|
+
var Store = require('./xsjs/security/Store');
|
|
20
|
+
var SAPPassport = require('@sap/e2e-trace').Passport;
|
|
21
|
+
var DbOptions = require('./xsjs/db/common/DbOptions');
|
|
22
|
+
|
|
23
|
+
var globalLibraryCache = {
|
|
24
|
+
store: function (libName, library, location) {
|
|
25
|
+
if (location === 'global') {
|
|
26
|
+
this.global[libName] = library;
|
|
27
|
+
} else {
|
|
28
|
+
logger.warn('Unsupported library cache mode: ' + location);
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
restore: function (context) {
|
|
32
|
+
var parentPackage = context;
|
|
33
|
+
Object.keys(globalLibraryCache.global).forEach(function (libName) {
|
|
34
|
+
libName.split('.').forEach(function (name, index, array) {
|
|
35
|
+
if (index === array.length - 1) {
|
|
36
|
+
context.libs[libName] = parentPackage[name] = globalLibraryCache.global[libName];
|
|
37
|
+
} else {
|
|
38
|
+
parentPackage = parentPackage[name] = parentPackage[name] || {};
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
global: {}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// # section Exports
|
|
47
|
+
|
|
48
|
+
module.exports.createRuntime = createRuntime;
|
|
49
|
+
module.exports.Runtime = Runtime;
|
|
50
|
+
|
|
51
|
+
// # Runtime class definition {
|
|
52
|
+
|
|
53
|
+
function Runtime() {
|
|
54
|
+
this.xsapp = {};
|
|
55
|
+
this.xsaccess = {};
|
|
56
|
+
this.xsjs = {};
|
|
57
|
+
this.xsjslib = {};
|
|
58
|
+
this.xsjobs = {};
|
|
59
|
+
this.xscfgd = {};
|
|
60
|
+
this.xshttpdest = {};
|
|
61
|
+
this.xsprivileges = {};
|
|
62
|
+
this.xsodata = [];
|
|
63
|
+
this._rejectedFiles = {};
|
|
64
|
+
this.rewriteRules = [];
|
|
65
|
+
this.redirectRules = [];
|
|
66
|
+
this.staticDirectories = [];
|
|
67
|
+
this.settings = {};
|
|
68
|
+
this._sandBoxHook = null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
Runtime.prototype.get = function (key) {
|
|
72
|
+
if (typeof key === 'undefined') {
|
|
73
|
+
return this.settings;
|
|
74
|
+
}
|
|
75
|
+
return this.settings[key];
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
Runtime.prototype.set = function (key, value) {
|
|
79
|
+
if (!value && key && typeof key === 'object') {
|
|
80
|
+
_.extend(this.settings, key);
|
|
81
|
+
} else {
|
|
82
|
+
this.settings[key] = value;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/*
|
|
87
|
+
Runtime.prototype.getScript = function (pathname) {
|
|
88
|
+
return this.xsjs[pathname];
|
|
89
|
+
};
|
|
90
|
+
*/
|
|
91
|
+
|
|
92
|
+
Runtime.prototype.getXsjsModule = function (pathname) {
|
|
93
|
+
return this.xsjs[pathname];
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
Runtime.prototype.getLibraryModule = function (libId) {
|
|
98
|
+
return this.xsjslib[libId];
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Executes XSJS script by getting it by <code>pathname</code> from the list of prepared
|
|
103
|
+
* scripts collected in <code>this.xsjs</code>
|
|
104
|
+
*
|
|
105
|
+
* @param pathname XSJS path - something like /my/script/location/file.xsjs
|
|
106
|
+
* @param xsruntime context that will be attached to the global context under '$' property
|
|
107
|
+
* @returns result context after execution
|
|
108
|
+
*/
|
|
109
|
+
Runtime.prototype.runXsjs = async function (pathname, xsruntime) {
|
|
110
|
+
var ecmaModule = this.getXsjsModule(pathname);
|
|
111
|
+
|
|
112
|
+
assert(ecmaModule, util.format('missing module for XSJS on path "%s"', pathname));
|
|
113
|
+
return await this._runModule(ecmaModule, xsruntime);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Executes XSJSLIB script by getting it by <code>pathname</code> from the list of prepared
|
|
118
|
+
* library scripts collected in <code>this.xsjslib</code>
|
|
119
|
+
*
|
|
120
|
+
* @param libId XSJSLIB package and name according to XS1 - something like my.script.location.file.xsjslib
|
|
121
|
+
* @param xsruntime context that will be attached to the global context under '$' property
|
|
122
|
+
* @returns result context after execution
|
|
123
|
+
*/
|
|
124
|
+
Runtime.prototype.runXsjslib = async function (libId, xsruntime) {
|
|
125
|
+
var ecmaModule = this.getLibraryModule(libId);
|
|
126
|
+
assert(ecmaModule, util.format('missing module for XSJSLIB with packagename "%s"', libId));
|
|
127
|
+
return await this._runModule(ecmaModule, xsruntime);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
Runtime.prototype._runModule = async function (ecmaModule, xsruntime) {
|
|
131
|
+
var context = this.createSandbox(xsruntime);
|
|
132
|
+
var module = await ecmaModule.getModule(context);
|
|
133
|
+
await module.evaluate();
|
|
134
|
+
return module;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* This method is provided for internal runtime stakeholders like XSUnit and provides
|
|
139
|
+
* possibility for consumers to enrich the sandbox with additional API provided in the global
|
|
140
|
+
* context.
|
|
141
|
+
*
|
|
142
|
+
* How it works: while executing some of run* methods like <code>runXsjslib</code>, <code>createSandbox</code> is called
|
|
143
|
+
* which prepares the base sandbox, calls the hook (if provided) and expects the hook to return valid sandbox.
|
|
144
|
+
* Finally script is executed using returned sandbox.
|
|
145
|
+
*
|
|
146
|
+
* @param hook function that will be called while sandbox is prepared
|
|
147
|
+
* @throws TypeError in case provided <code>hook</code> is not a function
|
|
148
|
+
*/
|
|
149
|
+
Runtime.prototype.setSandboxHook = function (hook) {
|
|
150
|
+
assert(hook, 'valid hook function expected');
|
|
151
|
+
if (!_.isFunction(hook)) {
|
|
152
|
+
throw new TypeError('valid sandbox hook expected');
|
|
153
|
+
}
|
|
154
|
+
this._sandBoxHook = hook;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
Runtime.prototype.createSandbox = function (xsruntime) {
|
|
158
|
+
var sbox = sandbox.create(xsruntime, this.get('context'));
|
|
159
|
+
if (this._sandBoxHook) {
|
|
160
|
+
sbox = this._sandBoxHook(sbox);
|
|
161
|
+
assert(sbox, 'valid sandbox should be returned by the sandbox hook');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return sbox;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
Runtime.prototype.importLibrary = async function (packagename, objectname, xsruntime) {
|
|
168
|
+
var libId;
|
|
169
|
+
if (objectname) {
|
|
170
|
+
libId = packagename + '.' + objectname;
|
|
171
|
+
} else if (packagename) {
|
|
172
|
+
if (path.extname(packagename) !== '.xsjslib') {
|
|
173
|
+
throw new VError('Library "%s" does not have ".xsjslib" extention', packagename);
|
|
174
|
+
}
|
|
175
|
+
var libPath;
|
|
176
|
+
if (packagename[0] === '/') {
|
|
177
|
+
libPath = packagename;
|
|
178
|
+
} else {
|
|
179
|
+
var callingXsjs = utils.getCallingXsjsRelativePath(this.get('rootDirs'));
|
|
180
|
+
var dir = path.dirname(callingXsjs);
|
|
181
|
+
libPath = path.normalize('/' + dir + '/' + packagename);
|
|
182
|
+
}
|
|
183
|
+
libId = utils.toResourceId(libPath);
|
|
184
|
+
} else {
|
|
185
|
+
throw new VError('Import library failed. No name specified.');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// check library cache
|
|
189
|
+
if (xsruntime.libs[libId]) {
|
|
190
|
+
return xsruntime.libs[libId];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
var ecmaModule = this.getLibraryModule(libId);
|
|
194
|
+
if (!ecmaModule) {
|
|
195
|
+
var xsjslib = suppLibs(this)[libId];
|
|
196
|
+
if (xsjslib) {
|
|
197
|
+
return xsjslib;
|
|
198
|
+
}
|
|
199
|
+
throw new VError('Library "%s" not found', libId);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// create package structure on sandbox
|
|
203
|
+
var sandbox = this.createSandbox(xsruntime);
|
|
204
|
+
var library = {};
|
|
205
|
+
|
|
206
|
+
attachLibraryToDollarObject(sandbox.$, libId, library);
|
|
207
|
+
await loadLibrary(sandbox, ecmaModule, library);
|
|
208
|
+
|
|
209
|
+
if (this.settings.libraryCache[libId]) {
|
|
210
|
+
globalLibraryCache.store(libId, library, this.settings.libraryCache[libId]);
|
|
211
|
+
}
|
|
212
|
+
return library;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
function attachLibraryToDollarObject(dollarObj, libId, library) {
|
|
216
|
+
// cache library
|
|
217
|
+
dollarObj.libs[libId] = library;
|
|
218
|
+
|
|
219
|
+
// attach library
|
|
220
|
+
var parent = dollarObj;
|
|
221
|
+
libId.split('.').forEach(function (name, idx, arr) {
|
|
222
|
+
if (idx + 1 === arr.length) {
|
|
223
|
+
parent[name] = library;
|
|
224
|
+
} else {
|
|
225
|
+
parent = parent[name] = parent[name] || {};
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async function loadLibrary(sandbox, ecmaModule, library) {
|
|
231
|
+
var module = await ecmaModule.getModule(sandbox);
|
|
232
|
+
await module.evaluate();
|
|
233
|
+
_.extend(library, module.namespace.default);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
Runtime.prototype.getDestination = function (packagename, objectname) {
|
|
237
|
+
return this.xshttpdest[packagename + '.' + objectname];
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
Runtime.prototype.getConfig = function (packagename, objectname) {
|
|
241
|
+
return this.xscfgd[packagename + '.' + objectname];
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
Runtime.prototype.encodeUser = function (user) {
|
|
245
|
+
var buffer = Buffer.from([user.id, user.pass].join(':'), 'utf8');
|
|
246
|
+
return buffer.toString('base64');
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
Runtime.prototype.decodeUser = function (encodedUser) {
|
|
250
|
+
var auth = Buffer.from(encodedUser, 'base64').toString('utf8').split(':');
|
|
251
|
+
return {
|
|
252
|
+
id: auth[0],
|
|
253
|
+
pass: auth[1]
|
|
254
|
+
};
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
// # Creating and attaching runtime context
|
|
258
|
+
Runtime.prototype.attachContext = function (req, res, traceOptions) {
|
|
259
|
+
var locale = res.locals.locale;
|
|
260
|
+
var webRequest = new xs.web.WebRequest(req, locale);
|
|
261
|
+
adjustRequestBody(req, webRequest);
|
|
262
|
+
var baseContext = this.createBaseContext(req, locale, traceOptions);
|
|
263
|
+
var webResponse = new xs.web.WebResponse(res, { runtime: this, context: baseContext, loggingContext: req.loggingContext, traceOptions: traceOptions, locale: locale, req: req });
|
|
264
|
+
|
|
265
|
+
req.$ = _.extend(baseContext, {
|
|
266
|
+
application: new xs.Application(locale, webResponse),
|
|
267
|
+
request: webRequest,
|
|
268
|
+
response: webResponse
|
|
269
|
+
});
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
function adjustRequestBody(req, webRequest) {
|
|
273
|
+
// only applicable for $.request
|
|
274
|
+
if (!req.body || !req.body.length) {
|
|
275
|
+
webRequest.body = undefined;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
Runtime.prototype.createBaseContext = function (req, locale, traceOptions) {
|
|
280
|
+
traceOptions.req = req;
|
|
281
|
+
var self = this;
|
|
282
|
+
var sapPassport = req.headers[SAPPassport.HEADER_NAME];
|
|
283
|
+
var traceObject = xs.trace.createTrace(traceOptions);
|
|
284
|
+
var context = {
|
|
285
|
+
import: async function (packagename, objectname) {
|
|
286
|
+
return await self.importLibrary(packagename, objectname, this);
|
|
287
|
+
},
|
|
288
|
+
require: function (packagename) {
|
|
289
|
+
return xs.require(packagename, self.get('rootDirs'));
|
|
290
|
+
},
|
|
291
|
+
trace: traceObject,
|
|
292
|
+
config: {
|
|
293
|
+
getObject: function (packagename, objectname) {
|
|
294
|
+
return self.getConfig(packagename, objectname);
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
net: {
|
|
298
|
+
http: new xs.net.HTTP(self.xshttpdest, destProvider.defineDestinationProviderFunction(self), sapPassport)
|
|
299
|
+
},
|
|
300
|
+
web: {
|
|
301
|
+
WebRequest: xs.web.WebRequest,
|
|
302
|
+
TupelList: xs.web.TupelList,
|
|
303
|
+
Body: xs.web.WebBody
|
|
304
|
+
},
|
|
305
|
+
libs: {}, // library cache
|
|
306
|
+
security: xs.security,
|
|
307
|
+
session: new xs.Session(req, locale),
|
|
308
|
+
util: xs.util
|
|
309
|
+
};
|
|
310
|
+
var mailOptions = this.get('mail');
|
|
311
|
+
if (mailOptions) {
|
|
312
|
+
context.net.Mail = xs.net.createMail(mailOptions);
|
|
313
|
+
context.net.SMTPConnection = xs.net.createSMTPConnection(mailOptions);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
var hanaDbOptions = this.get('hanaDbOptions');
|
|
317
|
+
if (hanaDbOptions) {
|
|
318
|
+
var dbReqOptions = hanaDbOptions.forRequest(req, locale);
|
|
319
|
+
context.db = new xs.db.DB(dbReqOptions);
|
|
320
|
+
context.hdb = new xs.db.HDB(dbReqOptions, traceObject._tracer);
|
|
321
|
+
context.text = xs.text.create(context.db);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
var secStoreDbOptions = this.get('secureStoreDbOptions');
|
|
325
|
+
if (secStoreDbOptions) {
|
|
326
|
+
var ssDbOptions = secStoreDbOptions.forRequest(req, locale);
|
|
327
|
+
context.security.Store = Store.createStore(ssDbOptions);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (this.get('jobs')) {
|
|
331
|
+
context.jobs = new xs.Jobs(self);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
globalLibraryCache.restore(context);
|
|
335
|
+
|
|
336
|
+
return context;
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
Runtime.prototype.cleanupContext = function (jsContext) {
|
|
340
|
+
|
|
341
|
+
if (jsContext.db) {
|
|
342
|
+
jsContext.db._closeAllConnections();
|
|
343
|
+
}
|
|
344
|
+
if (jsContext.hdb) {
|
|
345
|
+
jsContext.hdb._closeAllConnections();
|
|
346
|
+
}
|
|
347
|
+
if (jsContext.security.Store) {
|
|
348
|
+
jsContext.security.Store._closeAllConnections();
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
Runtime.prototype.triggerGc = function () {
|
|
353
|
+
if (global.gc) {
|
|
354
|
+
global.gc();
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
// # Jobs handling
|
|
359
|
+
|
|
360
|
+
Runtime.prototype.getJob = function (pathname) {
|
|
361
|
+
return this.xsjobs[pathname];
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
Runtime.prototype.getJobName = function (jobPath) {
|
|
365
|
+
var appConfig = this.get('appConfig');
|
|
366
|
+
return jobs.Job.buildName(appConfig.host, jobPath);
|
|
367
|
+
};
|
|
368
|
+
// } Runtime class definition
|
|
369
|
+
|
|
370
|
+
function normalizeFormData(formData) {
|
|
371
|
+
var result = _.defaults({}, formData, {
|
|
372
|
+
maxFilesSizeInBytes: 10485760 // default: 10MB
|
|
373
|
+
});
|
|
374
|
+
assert(typeof result.maxFilesSizeInBytes === 'number' && result.maxFilesSizeInBytes > 0);
|
|
375
|
+
return result;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function getSecStoreDbOptions(options, xsApplicationUser) {
|
|
379
|
+
if (options) {
|
|
380
|
+
delete options.connectWithLoggedUser;
|
|
381
|
+
delete options.sqlcc;
|
|
382
|
+
options.plan = 'securestore';
|
|
383
|
+
return new DbOptions(options, xsApplicationUser);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
async function* getAllFiles(dir) {
|
|
388
|
+
const dirents = await fsp.readdir(dir, { withFileTypes: true });
|
|
389
|
+
for (const dirent of dirents) {
|
|
390
|
+
const res = path.resolve(dir, dirent.name);
|
|
391
|
+
if (dirent.isDirectory()) {
|
|
392
|
+
yield* getAllFiles(res);
|
|
393
|
+
} else {
|
|
394
|
+
yield res;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
async function createRuntime(options) {
|
|
400
|
+
var rt = new Runtime();
|
|
401
|
+
|
|
402
|
+
options = options || {};
|
|
403
|
+
var appConfig = new AppConfig(options.port);
|
|
404
|
+
|
|
405
|
+
rt.set('hanaDbOptions', options.hana && new DbOptions(options.hana, options.xsApplicationUser));
|
|
406
|
+
rt.set('jobs', options.jobs);
|
|
407
|
+
rt.set('appConfig', appConfig);
|
|
408
|
+
rt.set('compression', options.compression === undefined || !!options.compression);
|
|
409
|
+
rt.set('libraryCache', options.libraryCache || {});
|
|
410
|
+
rt.set('mail', options.mail || {});
|
|
411
|
+
rt.set('formData', normalizeFormData(options.formData));
|
|
412
|
+
rt.set('destinationProvider', options.destinationProvider);
|
|
413
|
+
rt.set('secureStoreDbOptions', getSecStoreDbOptions(options.secureStore, options.xsApplicationUser));
|
|
414
|
+
rt.set('context', options.context);
|
|
415
|
+
|
|
416
|
+
if (options.redirectUrl) {
|
|
417
|
+
rt.redirectRules.push({
|
|
418
|
+
pathname: '/',
|
|
419
|
+
location: options.redirectUrl
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
var directories = options.rootDirs || [];
|
|
424
|
+
directories = directories.filter(function (dirPath) {
|
|
425
|
+
return !!dirPath;
|
|
426
|
+
}).map(function (unresolvedPath) {
|
|
427
|
+
return path.resolve(unresolvedPath);
|
|
428
|
+
});
|
|
429
|
+
if (!directories.length) {
|
|
430
|
+
directories.push(path.resolve(options.rootDir || 'lib'));
|
|
431
|
+
}
|
|
432
|
+
rt.set('rootDirs', directories);
|
|
433
|
+
|
|
434
|
+
for (var directory of directories) {
|
|
435
|
+
for await (const file of getAllFiles(directory)) {
|
|
436
|
+
var rel = path.relative(directory, file).replaceAll('\\', '/');
|
|
437
|
+
var pathname = '/' + rel;
|
|
438
|
+
var basename = path.basename(file);
|
|
439
|
+
if (basename === '.xsapp') {
|
|
440
|
+
xsapp.call(rt, pathname, file);
|
|
441
|
+
} else if (basename === '.xsaccess') {
|
|
442
|
+
xsaccess.call(rt, pathname, file);
|
|
443
|
+
} else if (basename === '.xsprivileges') {
|
|
444
|
+
xsprivileges.call(rt, pathname, file);
|
|
445
|
+
} else if (/\.xsjs$/.test(basename)) {
|
|
446
|
+
await xsjs.call(rt, pathname, file);
|
|
447
|
+
} else if (/\.xsjslib$/.test(basename)) {
|
|
448
|
+
await xsjslib.call(rt, pathname, file);
|
|
449
|
+
} else if (/\.xsodata$/.test(basename)) {
|
|
450
|
+
xsodata.call(rt, pathname, file);
|
|
451
|
+
} else if (/\.xscfgd$/.test(basename)) {
|
|
452
|
+
xscfgd.call(rt, pathname, file);
|
|
453
|
+
} else if (/\.xshttpdest$/.test(basename)) {
|
|
454
|
+
xshttpdest.call(rt, pathname, file);
|
|
455
|
+
} else if (/\.xsjob$/.test(basename)) {
|
|
456
|
+
xsjob.call(rt, pathname, file);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// all files have been processed, check jobs can be executed
|
|
462
|
+
validateJobs.call(rt);
|
|
463
|
+
|
|
464
|
+
if (!_.isEmpty(rt._rejectedFiles)) {
|
|
465
|
+
_.forEach(rt._rejectedFiles, function (err, filename) {
|
|
466
|
+
logger.error(err, 'File "%s" rejected', filename);
|
|
467
|
+
});
|
|
468
|
+
throw new Error('Some of the files in the working dir(s) were rejected. Check the logs.');
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
return rt;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function readFile(filename) {
|
|
475
|
+
return fs.readFileSync(filename, 'utf8');
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function readJson(filename) {
|
|
479
|
+
try {
|
|
480
|
+
return JSON.parse(readFile(filename));
|
|
481
|
+
} catch (err) {
|
|
482
|
+
this._rejectedFiles[filename] = err;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function readJsonThatCanBeEmpty(filename) {
|
|
487
|
+
try {
|
|
488
|
+
var content = readFile(filename);
|
|
489
|
+
if (_.isEmpty(_.trim(content))) {
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
return JSON.parse(content);
|
|
493
|
+
} catch (err) {
|
|
494
|
+
this._rejectedFiles[filename] = err;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function readXScfgd(filename) {
|
|
499
|
+
var sConfig = readFile(filename);
|
|
500
|
+
var lines = sConfig.split(/;\s*[\r\n]/);
|
|
501
|
+
|
|
502
|
+
if (/implements \S+/.test(lines[0])) {
|
|
503
|
+
lines.shift();
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
var sandbox = {};
|
|
507
|
+
var it;
|
|
508
|
+
|
|
509
|
+
for (var lnn in lines) {
|
|
510
|
+
var line = lines[lnn];
|
|
511
|
+
line = line.trim();
|
|
512
|
+
if (line === '') {
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
if (line[0] === '/' && line[1] === '/') {
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
var aProp = line.split('=');
|
|
520
|
+
if (aProp.length > 1) {
|
|
521
|
+
var key = aProp[0].trim();
|
|
522
|
+
var akey = key.split('.');
|
|
523
|
+
akey.pop();
|
|
524
|
+
|
|
525
|
+
var o = sandbox;
|
|
526
|
+
for (it = 0; it < akey.length; ++it) {
|
|
527
|
+
o = o[akey[it]] = o[akey[it]] || {};
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
var newScript = lines.join(';\r\n');
|
|
533
|
+
try {
|
|
534
|
+
var script = new vm.Script(newScript);
|
|
535
|
+
script.runInNewContext(sandbox);
|
|
536
|
+
|
|
537
|
+
return sandbox;
|
|
538
|
+
} catch (err) {
|
|
539
|
+
this._rejectedFiles[filename] = err;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
function readHTTPDestination(filename) {
|
|
544
|
+
var sandbox = {
|
|
545
|
+
none: null,
|
|
546
|
+
basic: 'basic',
|
|
547
|
+
AssertionTicket: 'AssertionTicket'
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
var properties = Object.getOwnPropertyNames(sandbox);
|
|
551
|
+
try {
|
|
552
|
+
var script = new vm.Script(readFile(filename));
|
|
553
|
+
script.runInNewContext(sandbox);
|
|
554
|
+
|
|
555
|
+
properties.forEach(function (property) {
|
|
556
|
+
delete sandbox[property];
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
return sandbox;
|
|
560
|
+
} catch (err) {
|
|
561
|
+
this._rejectedFiles[filename] = err;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
function xsapp(pathname, filename) {
|
|
566
|
+
pathname = path.dirname(pathname);
|
|
567
|
+
if (this.xsapp[pathname]) {
|
|
568
|
+
return warnIgnoredFile(pathname, filename);
|
|
569
|
+
}
|
|
570
|
+
var data = readJsonThatCanBeEmpty.call(this, filename);
|
|
571
|
+
if (!data) {
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
this.xsapp[pathname] = data;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function xsaccess(pathname, filename) {
|
|
578
|
+
pathname = path.dirname(pathname);
|
|
579
|
+
if (this.xsaccess[pathname]) {
|
|
580
|
+
return warnIgnoredFile(pathname, filename);
|
|
581
|
+
}
|
|
582
|
+
var data = readJsonThatCanBeEmpty.call(this, filename);
|
|
583
|
+
if (!data) {
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
this.xsaccess[pathname] = data;
|
|
587
|
+
var dirname = path.dirname(filename);
|
|
588
|
+
if (data.exposed) {
|
|
589
|
+
logger.info('Adding static handler for "%s" in "%s"', pathname,
|
|
590
|
+
dirname);
|
|
591
|
+
this.staticDirectories.push({
|
|
592
|
+
pathname: pathname,
|
|
593
|
+
dirname: dirname
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
var rewriteRules = data.rewrite_rules;
|
|
597
|
+
if (rewriteRules) {
|
|
598
|
+
this.rewriteRules[pathname] = [];
|
|
599
|
+
rewriteRules.forEach(function (rule) {
|
|
600
|
+
logger.info('Adding rewrite rule from "%s" to "%s"', rule.source, rule.target);
|
|
601
|
+
this.rewriteRules[pathname].push({
|
|
602
|
+
regex: new RegExp(rule.source + '$'),
|
|
603
|
+
replacement: rule.target
|
|
604
|
+
});
|
|
605
|
+
}, this);
|
|
606
|
+
}
|
|
607
|
+
if (data.authentication) {
|
|
608
|
+
logger.warn('Authentication not yet supported');
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function xsprivileges(pathname, filename) {
|
|
613
|
+
pathname = path.dirname(pathname);
|
|
614
|
+
if (this.xsprivileges[pathname]) {
|
|
615
|
+
return warnIgnoredFile(pathname, filename);
|
|
616
|
+
}
|
|
617
|
+
var data = readJson.call(this, filename);
|
|
618
|
+
if (!data) {
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
this.xsprivileges[pathname] = data;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
function xsodata(pathname, filename) {
|
|
625
|
+
if (this.xsodata[pathname]) {
|
|
626
|
+
return warnIgnoredFile(pathname, filename);
|
|
627
|
+
}
|
|
628
|
+
this.xsodata[pathname] = filename;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
async function xsjs(pathname, filename) {
|
|
632
|
+
if (this.xsjs[pathname]) {
|
|
633
|
+
return warnIgnoredFile(pathname, filename);
|
|
634
|
+
}
|
|
635
|
+
let ecmaModule = new EcmaModule(filename);
|
|
636
|
+
try {
|
|
637
|
+
await ecmaModule.getModule(vm.createContext({'$': {}}));
|
|
638
|
+
} catch (err) {
|
|
639
|
+
this._rejectedFiles[filename] = err;
|
|
640
|
+
}
|
|
641
|
+
this.xsjs[pathname] = ecmaModule; // readScript.call(this, filename);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
async function xsjslib(pathname, filename) {
|
|
645
|
+
if (this.xsjslib[utils.toResourceId(pathname)]) {
|
|
646
|
+
return warnIgnoredFile(pathname, filename);
|
|
647
|
+
}
|
|
648
|
+
let ecmaModule = new EcmaModule(filename);
|
|
649
|
+
try {
|
|
650
|
+
await ecmaModule.getModule(vm.createContext({'$': {}}));
|
|
651
|
+
} catch (err) {
|
|
652
|
+
this._rejectedFiles[filename] = err;
|
|
653
|
+
}
|
|
654
|
+
this.xsjslib[utils.toResourceId(pathname)] = ecmaModule; // readScript.call(this, filename);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function xscfgd(pathname, filename) {
|
|
658
|
+
if (this.xscfgd[utils.toResourceId(pathname)]) {
|
|
659
|
+
return warnIgnoredFile(pathname, filename);
|
|
660
|
+
}
|
|
661
|
+
this.xscfgd[utils.toResourceId(pathname)] = readXScfgd.call(this, filename);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
function xshttpdest(pathname, filename) {
|
|
665
|
+
if (this.xshttpdest[utils.toResourceId(pathname)]) {
|
|
666
|
+
return warnIgnoredFile(pathname, filename);
|
|
667
|
+
}
|
|
668
|
+
this.xshttpdest[utils.toResourceId(pathname)] = readHTTPDestination.call(this, filename);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
function xsjob(pathname, filename) {
|
|
672
|
+
if (this.xsjobs[pathname]) {
|
|
673
|
+
return warnIgnoredFile(pathname, filename);
|
|
674
|
+
}
|
|
675
|
+
try {
|
|
676
|
+
var data = readJson.call(this, filename);
|
|
677
|
+
if (!data) {
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
this.xsjobs[pathname] = new jobs.Job(data, pathname, this.get('rootDirs'));
|
|
681
|
+
} catch (err) {
|
|
682
|
+
this._rejectedFiles[filename] = err;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
function warnIgnoredFile(pathname, filename) {
|
|
687
|
+
logger.warn(util.format('Ignoring %s - file with the same path (%s) is present in an already loaded rootDir', filename, pathname));
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
function validateJobs() {
|
|
691
|
+
var self = this;
|
|
692
|
+
Object.keys(self.xsjobs).forEach(function (key) {
|
|
693
|
+
var job = self.xsjobs[key];
|
|
694
|
+
job.active = true;
|
|
695
|
+
if (!job.action.isJavaScript()) {
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
var scriptPath = job.action.getScriptPath();
|
|
699
|
+
if (!self.getXsjsModule(scriptPath)) {
|
|
700
|
+
job.active = false;
|
|
701
|
+
var message = util.format('XSJS file "%s" required by job "%s" was not found', scriptPath, job.urlPath);
|
|
702
|
+
self._rejectedFiles[key] = new Error(message);
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
class EcmaModule {
|
|
709
|
+
constructor(fileName) {
|
|
710
|
+
this.fileName = fileName;
|
|
711
|
+
this.moduleSource = null;
|
|
712
|
+
this.cachedData = undefined;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
async getModule(context) {
|
|
716
|
+
if (!this.moduleSource) {
|
|
717
|
+
let xsjsfh;
|
|
718
|
+
try {
|
|
719
|
+
xsjsfh = await fsp.open(this.fileName);
|
|
720
|
+
this.moduleSource = await xsjsfh.readFile({encoding: 'utf-8'});
|
|
721
|
+
} finally {
|
|
722
|
+
if (xsjsfh) {
|
|
723
|
+
await xsjsfh.close();
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
}
|
|
728
|
+
// var moduleContext = vm.createContext(context);
|
|
729
|
+
var module = new vm.SourceTextModule(this.moduleSource, {
|
|
730
|
+
identifier: this.fileName,
|
|
731
|
+
context: context,
|
|
732
|
+
cachedData: this.cachedData
|
|
733
|
+
});
|
|
734
|
+
if (!this.cachedData) {
|
|
735
|
+
this.cachedData = module.createCachedData();
|
|
736
|
+
}
|
|
737
|
+
await module.link(() => {}); // TODO - check if we need linker
|
|
738
|
+
return module;
|
|
739
|
+
}
|
|
740
|
+
}
|