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 | 'use strict';
|
28 |
|
29 | const { Buffer } = require('buffer');
|
30 | const errors = require('./errors.js');
|
31 | const process = require('process');
|
32 | const util = require('util');
|
33 | const types = require('./types.js');
|
34 |
|
35 |
|
36 | let packageJSON;
|
37 | try {
|
38 | packageJSON = require('../package.json');
|
39 | } catch (err) {
|
40 | errors.throwErr(errors.ERR_MISSING_FILE, 'package.json');
|
41 | }
|
42 | const PACKAGE_JSON_VERSION = packageJSON.version;
|
43 |
|
44 |
|
45 | const RELEASE_DIR = 'build/Release';
|
46 |
|
47 |
|
48 | const BINARY_FILE = 'oracledb-' + PACKAGE_JSON_VERSION + '-' + process.platform + '-' + process.arch + '.node';
|
49 |
|
50 |
|
51 | const BUILD_FILE = 'oracledb.node';
|
52 |
|
53 |
|
54 | const STAGING_DIR = 'package/Staging';
|
55 |
|
56 |
|
57 | function getInstallURL() {
|
58 | return ('Node-oracledb installation instructions: https://node-oracledb.readthedocs.io/en/latest/user_guide/installation.html');
|
59 | }
|
60 |
|
61 |
|
62 |
|
63 | function getInstallHelp() {
|
64 | let arch, url;
|
65 | let mesg = getInstallURL() + '\n';
|
66 | if (process.platform === 'linux') {
|
67 | if (process.arch === 'x64') {
|
68 | url = 'https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html\n';
|
69 | arch = '64-bit';
|
70 | } else if (process.arch === 'x32') {
|
71 | url = 'https://www.oracle.com/database/technologies/instant-client/linux-x86-32-downloads.html\n';
|
72 | arch = '32-bit';
|
73 | } else {
|
74 | url = 'https://www.oracle.com/database/technologies/instant-client.html\n';
|
75 | arch = process.arch;
|
76 | }
|
77 | mesg += 'You must have Linux ' + arch + ' Oracle Client libraries configured with ldconfig, or in LD_LIBRARY_PATH.\n';
|
78 | mesg += 'If you do not have Oracle Database on this computer, then install the Instant Client Basic or Basic Light package from \n';
|
79 | mesg += url;
|
80 | } else if (process.platform === 'darwin') {
|
81 | if (process.arch === 'x64') {
|
82 | url = 'https://www.oracle.com/database/technologies/instant-client/macos-intel-x86-downloads.html\n';
|
83 | arch = '64-bit';
|
84 | } else {
|
85 | url = 'https://www.oracle.com/database/technologies/instant-client.html\n';
|
86 | arch = process.arch;
|
87 | }
|
88 | mesg += 'You must have macOS ' + arch + ' Oracle Instant Client Basic or Basic Light package libraries in\n';
|
89 | mesg += '/usr/local/lib or set by calling oracledb.initOracleClient({libDir: "/my/instant_client_directory"}).\n';
|
90 | mesg += 'Oracle Instant Client can be downloaded from ' + url;
|
91 | } else if (process.platform === 'win32') {
|
92 | if (process.arch === 'x64') {
|
93 | url = 'https://www.oracle.com/database/technologies/instant-client/winx64-64-downloads.html\n';
|
94 | arch = '64-bit';
|
95 | } else if (process.arch === 'x32') {
|
96 | url = 'https://www.oracle.com/database/technologies/instant-client/microsoft-windows-32-downloads.html\n';
|
97 | arch = '32-bit';
|
98 | } else {
|
99 | url = 'https://www.oracle.com/database/technologies/instant-client.html\n';
|
100 | arch = process.arch;
|
101 | }
|
102 | mesg += 'You must have Windows ' + arch + ' Oracle Client libraries in your PATH environment variable.\n';
|
103 | mesg += 'If you do not have Oracle Database on this computer, then install the Instant Client Basic or Basic Light package from\n';
|
104 | mesg += url;
|
105 | mesg += 'A Microsoft Visual Studio Redistributable suitable for your Oracle client library version must be available.\n';
|
106 | } else {
|
107 | url = 'https://www.oracle.com/database/technologies/instant-client.html\n';
|
108 | mesg += 'You must have ' + process.arch + ' Oracle Client libraries in your operating system library search path.\n';
|
109 | mesg += 'If you do not have Oracle Database on this computer, then install an Instant Client Basic or Basic Light package from: \n';
|
110 | mesg += url;
|
111 | }
|
112 | return mesg;
|
113 | }
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 | function callbackify(func) {
|
121 | const wrapper = function() {
|
122 |
|
123 |
|
124 |
|
125 | if (typeof arguments[arguments.length - 1] !== 'function') {
|
126 | return func.apply(this, arguments).catch(function stackCapture(err) {
|
127 | throw errors.transformErr(err, stackCapture);
|
128 | });
|
129 | }
|
130 |
|
131 |
|
132 | const args = Array.prototype.slice.call(arguments, 0, arguments.length - 1);
|
133 | const cb = arguments[arguments.length - 1];
|
134 | func.apply(this, args).then(function(result) {
|
135 | cb(null, result);
|
136 | }, function stackCapture(err) {
|
137 | cb(errors.transformErr(err, stackCapture));
|
138 | });
|
139 | };
|
140 | if (func.name) {
|
141 | Object.defineProperty(wrapper, 'name', { value: func.name });
|
142 | }
|
143 | return wrapper;
|
144 | }
|
145 |
|
146 |
|
147 |
|
148 | function serialize(func) {
|
149 | return async function() {
|
150 |
|
151 | let connImpl;
|
152 |
|
153 |
|
154 |
|
155 |
|
156 | if (this._impl) {
|
157 | connImpl = this._impl._getConnImpl();
|
158 | await connImpl._acquireLock();
|
159 | }
|
160 |
|
161 |
|
162 |
|
163 |
|
164 | try {
|
165 | return await func.apply(this, arguments);
|
166 | } finally {
|
167 | if (connImpl)
|
168 | connImpl._releaseLock();
|
169 | }
|
170 | };
|
171 | }
|
172 |
|
173 | function preventConcurrent(func, errorCode) {
|
174 | return async function() {
|
175 | if (this._isActive)
|
176 | errors.throwErr(errorCode);
|
177 | this._isActive = true;
|
178 | try {
|
179 | return await func.apply(this, arguments);
|
180 | } finally {
|
181 | this._isActive = false;
|
182 | }
|
183 | };
|
184 | }
|
185 |
|
186 |
|
187 |
|
188 |
|
189 |
|
190 |
|
191 | function wrapFns(proto) {
|
192 | let nameIndex = 1;
|
193 | let preventConcurrentErrorCode;
|
194 | if (typeof arguments[1] === 'number') {
|
195 | nameIndex = 2;
|
196 | preventConcurrentErrorCode = arguments[1];
|
197 | }
|
198 | for (let i = nameIndex; i < arguments.length; i++) {
|
199 | const name = arguments[i];
|
200 | const f = proto[name];
|
201 | if (preventConcurrentErrorCode) {
|
202 | proto[name] = callbackify(preventConcurrent(serialize(f),
|
203 | preventConcurrentErrorCode));
|
204 | } else
|
205 | proto[name] = callbackify(serialize(f));
|
206 | }
|
207 | }
|
208 |
|
209 | function isArrayOfStrings(value) {
|
210 | if (!Array.isArray(value))
|
211 | return false;
|
212 | for (let i = 0; i < value.length; i++) {
|
213 | if (typeof value[i] !== 'string')
|
214 | return false;
|
215 | }
|
216 | return true;
|
217 | }
|
218 |
|
219 | function isObject(value) {
|
220 | return value !== null && typeof value === 'object';
|
221 | }
|
222 |
|
223 | function isObjectOrArray(value) {
|
224 | return (value !== null && typeof value === 'object') || Array.isArray(value);
|
225 | }
|
226 |
|
227 | function isShardingKey(value) {
|
228 | if (!Array.isArray(value))
|
229 | return false;
|
230 | for (let i = 0; i < value.length; i++) {
|
231 | const element = value[i];
|
232 | const ok = typeof element === 'string' ||
|
233 | typeof element === 'number' || Buffer.isBuffer(element) ||
|
234 | util.isDate(element);
|
235 | if (!ok)
|
236 | return false;
|
237 | }
|
238 | return true;
|
239 | }
|
240 |
|
241 | function isSodaDocument(value) {
|
242 | return (value != null && value._sodaDocumentMarker);
|
243 | }
|
244 |
|
245 | function isXid(value) {
|
246 | return (isObject(value) && Number.isInteger(value.formatId) &&
|
247 | (Buffer.isBuffer(value.globalTransactionId) ||
|
248 | typeof value.globalTransactionId === 'string') &&
|
249 | (Buffer.isBuffer(value.branchQualifier) ||
|
250 | typeof value.branchQualifier === 'string'));
|
251 | }
|
252 |
|
253 | function verifySodaDoc(content) {
|
254 | if (isSodaDocument(content))
|
255 | return content._impl;
|
256 | errors.assertParamValue(isObject(content), 1);
|
257 | return Buffer.from(JSON.stringify(content));
|
258 | }
|
259 |
|
260 | function isTokenExpired(token) {
|
261 | errors.assert(typeof token === 'string', errors.ERR_TOKEN_BASED_AUTH);
|
262 | if (token.split('.')[1] === undefined) {
|
263 | errors.throwErr(errors.ERR_TOKEN_BASED_AUTH);
|
264 | }
|
265 |
|
266 | const base64Url = token.split('.')[1];
|
267 | const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
268 | const buff = Buffer.from(base64, 'base64');
|
269 | const payloadInit = buff.toString('ascii');
|
270 |
|
271 | let expiry = JSON.parse(payloadInit).exp;
|
272 | errors.assert(expiry != undefined, errors.ERR_TOKEN_BASED_AUTH);
|
273 | expiry = expiry * 1000;
|
274 |
|
275 | return (new Date().getTime() > expiry);
|
276 | }
|
277 |
|
278 | function isTokenValid(accessToken) {
|
279 | switch (typeof accessToken) {
|
280 | case 'string':
|
281 | if (accessToken === '') {
|
282 | errors.throwErr(errors.ERR_TOKEN_BASED_AUTH);
|
283 | }
|
284 |
|
285 | return !isTokenExpired(accessToken);
|
286 | case 'object':
|
287 | if (accessToken.token === undefined ||
|
288 | accessToken.token === '' ||
|
289 | accessToken.privateKey === undefined ||
|
290 | accessToken.privateKey === '') {
|
291 | errors.throwErr(errors.ERR_TOKEN_BASED_AUTH);
|
292 | }
|
293 |
|
294 | return !isTokenExpired(accessToken.token);
|
295 | default:
|
296 | errors.throwErr(errors.ERR_TOKEN_BASED_AUTH);
|
297 | }
|
298 | }
|
299 |
|
300 | function denormalizePrivateKey(privateKey) {
|
301 | privateKey = privateKey.replace(/\n/g, '');
|
302 | privateKey = privateKey.replace('-----BEGIN PRIVATE KEY-----', '');
|
303 | privateKey = privateKey.replace('-----END PRIVATE KEY-----', '');
|
304 | return privateKey;
|
305 | }
|
306 |
|
307 |
|
308 |
|
309 |
|
310 |
|
311 |
|
312 |
|
313 |
|
314 |
|
315 | function addTypeProperties(obj, attrName) {
|
316 | const clsAttrName = attrName + "Class";
|
317 | const nameAttrName = attrName + "Name";
|
318 | const cls = obj[clsAttrName];
|
319 | let dbType = obj[attrName];
|
320 | if (typeof dbType === 'number') {
|
321 | dbType = obj[attrName] = types.getTypeByNum(dbType);
|
322 | }
|
323 | if (cls) {
|
324 | obj[nameAttrName] = cls.prototype.fqn;
|
325 | } else if (dbType) {
|
326 | obj[nameAttrName] = dbType.columnTypeName;
|
327 | }
|
328 | }
|
329 |
|
330 |
|
331 |
|
332 |
|
333 |
|
334 |
|
335 |
|
336 | function isVectorValue(value) {
|
337 | return (value instanceof Float32Array ||
|
338 | value instanceof Float64Array ||
|
339 | value instanceof Int8Array);
|
340 | }
|
341 |
|
342 |
|
343 |
|
344 |
|
345 |
|
346 |
|
347 |
|
348 | function makeDate(useLocal, year, month, day, hour, minute,
|
349 | second, fseconds, offset) {
|
350 | if (useLocal) {
|
351 | return new Date(year, month - 1, day, hour, minute, second, fseconds);
|
352 | }
|
353 | return new Date(Date.UTC(year, month - 1, day, hour, minute, second,
|
354 | fseconds) - offset * 60000);
|
355 | }
|
356 |
|
357 |
|
358 | module.exports = {
|
359 | BINARY_FILE,
|
360 | BUILD_FILE,
|
361 | PACKAGE_JSON_VERSION,
|
362 | RELEASE_DIR,
|
363 | STAGING_DIR,
|
364 | addTypeProperties,
|
365 | callbackify,
|
366 | denormalizePrivateKey,
|
367 | getInstallURL,
|
368 | getInstallHelp,
|
369 | isArrayOfStrings,
|
370 | isObject,
|
371 | isObjectOrArray,
|
372 | isShardingKey,
|
373 | isSodaDocument,
|
374 | isTokenExpired,
|
375 | isTokenValid,
|
376 | isVectorValue,
|
377 | isXid,
|
378 | makeDate,
|
379 | preventConcurrent,
|
380 | serialize,
|
381 | verifySodaDoc,
|
382 | wrapFns
|
383 | };
|