'use strict'; // ////////////////////////////////////////////////////////////////////////////// // / @brief JavaScript base module // / // / @file // / // / DISCLAIMER // / // / Copyright 2004-2013 triAGENS GmbH, Cologne, Germany // / // / Licensed under the Apache License, Version 2.0 (the "License") // / you may not use this file except in compliance with the License. // / You may obtain a copy of the License at // / // / http://www.apache.org/licenses/LICENSE-2.0 // / // / Unless required by applicable law or agreed to in writing, software // / distributed under the License is distributed on an "AS IS" BASIS, // / WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // / See the License for the specific language governing permissions and // / limitations under the License. // / // / Copyright holder is triAGENS GmbH, Cologne, Germany // / // / @author Dr. Frank Celler // / @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany // ////////////////////////////////////////////////////////////////////////////// var internal = require('internal'); var fs = require('fs'); // ////////////////////////////////////////////////////////////////////////////// // / @brief errors // ////////////////////////////////////////////////////////////////////////////// Object.keys(internal.errors).forEach(function (key) { exports[key] = internal.errors[key].code; }); exports.aql = function (strings, ...args) { const bindVars = {}; const bindVals = []; let query = strings[0]; for (let i = 0; i < args.length; i++) { const rawValue = args[i]; let value = rawValue; if (rawValue && typeof rawValue.toAQL === 'function') { query += `${rawValue.toAQL()}${strings[i + 1]}`; continue; } const index = bindVals.indexOf(rawValue); const isKnown = index !== -1; let name = `value${isKnown ? index : bindVals.length}`; if (rawValue && rawValue.isArangoCollection) { name = `@${name}`; value = rawValue.name(); } if (!isKnown) { bindVals.push(rawValue); bindVars[name] = value; } query += `@${name}${strings[i + 1]}`; } return {query, bindVars}; }; exports.errors = internal.errors; // ////////////////////////////////////////////////////////////////////////////// // / @brief ArangoError // ////////////////////////////////////////////////////////////////////////////// exports.ArangoError = internal.ArangoError; // ////////////////////////////////////////////////////////////////////////////// // / @brief defines a module // ////////////////////////////////////////////////////////////////////////////// exports.defineModule = function (path, file) { var content; var m; var mc; content = fs.read(file); mc = internal.db._collection('_modules'); if (mc === null) { mc = internal.db._create('_modules', { isSystem: true }); } path = module.normalize(path); m = mc.firstExample({ path: path }); if (m === null) { mc.save({ path: path, content: content }); }else { mc.replace(m, { path: path, content: content }); } }; // ////////////////////////////////////////////////////////////////////////////// // / @brief normalizeURL // / // / If @FA{path} starts with "." or "..", then it is a relative path. // / Otherwise it is an absolute path. Normalizing will remove `//`, // / `/./`, `/../` from the url - expect in the beginning, where it keeps // / `../` and or at most one `./`. // / // / If @FA{path} is empty, the url `./` will be returned. // ////////////////////////////////////////////////////////////////////////////// exports.normalizeURL = function (path) { var i; var n; var p; var q; var r; var x; if (path === '') { return './'; } p = path.split('/'); // relative path if (p[0] === '.' || p[0] === '..') { r = p[0] + '/'; p.shift(); q = p; } // absolute path else if (p[0] === '') { r = '/'; p.shift(); q = p; } // assume that the path is relative else { r = './'; q = p; } // normalize path n = []; for (i = 0; i < q.length; ++i) { x = q[i]; if (x === '..') { if (n.length === 0) { if (r === '../') { n.push(x); } else if (r === './') { r = '../'; }else { throw "cannot use '..' to escape top-level-directory"; } } else if (n[n.length - 1] === '..') { n.push(x); }else { n.pop(); } } else if (x !== '' && x !== '.') { n.push(x); } } return r + n.join('/'); }; // ////////////////////////////////////////////////////////////////////////////// // / @brief inspect // ////////////////////////////////////////////////////////////////////////////// exports.inspect = internal.inspect; // ////////////////////////////////////////////////////////////////////////////// // / @brief output // / // / In order to allow "capture" output to work, we cannot assigne the // / function here. // ////////////////////////////////////////////////////////////////////////////// exports.output = function () { internal.output.apply(internal.output, arguments); }; // ////////////////////////////////////////////////////////////////////////////// // / @brief print // ////////////////////////////////////////////////////////////////////////////// exports.print = internal.print; // ////////////////////////////////////////////////////////////////////////////// // / @brief printf // ////////////////////////////////////////////////////////////////////////////// exports.printf = internal.printf; // ////////////////////////////////////////////////////////////////////////////// // / @brief sprintf // ////////////////////////////////////////////////////////////////////////////// exports.sprintf = internal.sprintf; // ////////////////////////////////////////////////////////////////////////////// // / @brief printObject // ////////////////////////////////////////////////////////////////////////////// exports.printObject = internal.printObject; // ////////////////////////////////////////////////////////////////////////////// // / @brief 2D ASCII table printing // ////////////////////////////////////////////////////////////////////////////// exports.printTable = function (list, columns, options) { options = options || { }; if (options.totalString === undefined) { options.totalString = '%s document(s)\n'; } var pad = '...'; var descriptions, matrix, col, what, j; if (columns === undefined) { what = list[0]; } else if (Array.isArray(columns)) { what = { }; columns.forEach(function (col) { what[col] = null; }); }else { what = columns; } j = 0; descriptions = []; matrix = [ [] ]; for (col in what) { if (what.hasOwnProperty(col)) { var fixedLength = null; if (columns && columns.hasOwnProperty(col) && columns[col] > 0) { fixedLength = columns[col] >= pad.length ? columns[col] : pad.length; } // header var name = col; // rename header? if (options.hasOwnProperty('rename')) { if (options.rename.hasOwnProperty(col)) { name = options.rename[col]; } } descriptions.push({ id: col, fixedLength: fixedLength, length: fixedLength || name.length }); matrix[0][j++] = name; } } // determine values & max widths list.forEach(function (row, i) { matrix[i + 1] = []; descriptions.forEach(function (col) { if (row.hasOwnProperty(col.id)) { var value; if (options.prettyStrings && typeof row[col.id] === 'string') { value = row[col.id]; }else { value = JSON.stringify(row[col.id]) || ''; } matrix[i + 1].push(value); if (value.length > col.length && ! col.fixedLength) { col.length = Math.min(value.length, 100); } }else { // undefined matrix[i + 1].push(''); } }); }); var divider = function () { var parts = []; descriptions.forEach(function (desc) { parts.push(exports.stringPadding('', desc.length, '-', 'r')); }); if (options.framed) { return '+-' + parts.join('-+-') + '-+\n'; } return parts.join(' ') + '\n'; }; var compose = function () { var result = ''; if (options.framed) { result += divider(); } matrix.forEach(function (row, i) { var parts = []; row.forEach(function (col, j) { var len = descriptions[j].length, value = row[j]; if (value.length > len) { value = value.substr(0, len - pad.length) + pad; } parts.push(exports.stringPadding(value, len, ' ', 'r')); }); if (options.framed) { result += '| ' + parts.join(' | ') + ' |\n'; }else { result += parts.join(' ') + '\n'; } if (i === 0) { result += divider(); } }); result += divider(); if (! options.hideTotal) { result += internal.sprintf(options.totalString, String(list.length)); } return result; }; if (! Array.isArray(list)) { // not an array return; } if (list.length === 0) { exports.print(options.emptyString || 'no document(s)'); }else { exports.print(compose()); } }; // ////////////////////////////////////////////////////////////////////////////// // / @brief stringPadding // ////////////////////////////////////////////////////////////////////////////// exports.stringPadding = function (str, len, pad, dir) { // yes, this is more code than new Array(length).join(chr), but it makes jslint happy function fill (length, chr) { var result = '', i; for (i = 0; i < length; ++i) { result += chr; } return result; } if (typeof (len) === 'undefined') { len = 0; } if (typeof (pad) === 'undefined') { pad = ' '; } if (len + 1 >= str.length) { switch (dir || 'r') { // LEFT case 'l': str = fill(len + 1 - str.length, pad) + str; break; // BOTH case 'b': var padlen = len - str.length; var right = Math.ceil(padlen / 2); var left = padlen - right; str = fill(left + 1, pad) + str + fill(right + 1, pad); break; default: str = str + fill(len + 1 - str.length, pad); break; } } return str; }; // ////////////////////////////////////////////////////////////////////////////// // / @brief throws an error in case a download failed // ////////////////////////////////////////////////////////////////////////////// exports.throwDownloadError = function (msg) { throw new exports.ArangoError({ errorNum: exports.errors.ERROR_SERVICE_DOWNLOAD_FAILED.code, errorMessage: exports.errors.ERROR_SERVICE_DOWNLOAD_FAILED.message + ': ' + String(msg) }); }; // ////////////////////////////////////////////////////////////////////////////// // / @brief throws an error in case of missing file // ////////////////////////////////////////////////////////////////////////////// exports.throwFileNotFound = function (msg) { throw new exports.ArangoError({ errorNum: exports.errors.ERROR_FILE_NOT_FOUND.code, errorMessage: exports.errors.ERROR_FILE_NOT_FOUND.message + ': ' + String(msg) }); }; // ////////////////////////////////////////////////////////////////////////////// // / @brief throws an error in case of a bad parameter // ////////////////////////////////////////////////////////////////////////////// exports.throwBadParameter = function (msg) { throw new exports.ArangoError({ errorNum: exports.errors.ERROR_BAD_PARAMETER.code, errorMessage: exports.errors.ERROR_BAD_PARAMETER.message + ': ' + String(msg) }); }; // ////////////////////////////////////////////////////////////////////////////// // / @brief checks parameter, throws an error if missing // ////////////////////////////////////////////////////////////////////////////// exports.checkParameter = function (usage, descs, vars) { var i; for (i = 0; i < descs.length; ++i) { var desc = descs[i]; if (typeof vars[i] === 'undefined') { exports.throwBadParameter(desc[0] + ' missing, usage: ' + usage); } if (typeof vars[i] !== desc[1]) { exports.throwBadParameter(desc[0] + " should be a '" + desc[1] + "', " + "not '" + (typeof vars[i]) + "'"); } } }; // ////////////////////////////////////////////////////////////////////////////// // / @brief generate info message for newer version(s) available // ////////////////////////////////////////////////////////////////////////////// exports.checkAvailableVersions = function (version) { var console = require('console'); var log; if (require('@arangodb').isServer) { log = console.info; }else { log = internal.print; } if (version === undefined) { version = internal.version; } if (version.match(/beta|alpha|preview|devel/) !== null) { log("You are using an alpha/beta/preview version ('" + version + "') of ArangoDB"); return; } try { var u = 'https://www.arangodb.com/repositories/versions.php?version=' + version + '&os=' + internal.platform; var d = internal.download(u, '', {timeout: 5}); var v = JSON.parse(d.body); if (v.hasOwnProperty('bugfix')) { log("Please note that a new bugfix version '" + v.bugfix.version + "' is available"); } if (v.hasOwnProperty('minor')) { log("Please note that a new minor version '" + v.minor.version + "' is available"); } if (v.hasOwnProperty('major')) { log("Please note that a new major version '" + v.major.version + "' is available"); } } catch (err) { if (console && console.debug) { console.debug('cannot check for newer version: ', err.stack); } } };