join-monster
Version:
A GraphQL to SQL query execution layer for batch data fetching.
194 lines (193 loc) • 6.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.buildWhereFunction = buildWhereFunction;
exports.compileSqlAST = compileSqlAST;
exports.cursorToObj = cursorToObj;
exports.emphasize = emphasize;
exports.ensure = ensure;
exports.getConfigFromSchemaObject = getConfigFromSchemaObject;
exports.handleUserDbCall = handleUserDbCall;
exports.inspect = inspect;
exports.isEmptyArray = isEmptyArray;
exports.last = last;
exports.maybeQuote = maybeQuote;
exports.objToCursor = objToCursor;
exports.unthunk = unthunk;
exports.validateSqlAST = validateSqlAST;
exports.wrap = wrap;
var _util = _interopRequireDefault(require("util"));
var _assert = _interopRequireDefault(require("assert"));
var _nesthydrationjs = require("@stem/nesthydrationjs");
var _dispatcher = _interopRequireDefault(require("./stringifiers/dispatcher"));
var _resolveUnions = _interopRequireDefault(require("./resolve-unions"));
var _deprecate = _interopRequireDefault(require("deprecate"));
var _defineObjectShape = _interopRequireDefault(require("./define-object-shape"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const debug = require('debug')('join-monster');
function emphasize(str, colorCode = 33) {
return `\n\x1b[1;${colorCode}m${str}\x1b[0m\n`;
}
function inspect(obj, options = {}) {
return _util.default.inspect(obj, {
depth: 12,
...options
});
}
function last(arr) {
return arr[arr.length - 1];
}
function wrap(maybeArr) {
if (maybeArr.constructor === Array) {
return maybeArr;
}
return [maybeArr];
}
function isEmptyArray(val) {
return Array.isArray(val) && val.length === 0;
}
function ensure(obj, prop, name) {
if (!obj[prop]) {
throw new Error(`property "${prop}" must be defined on object: ${name || _util.default.inspect(obj)}`);
}
return obj[prop];
}
function unthunk(val, ...args) {
return typeof val === 'function' ? val(...args) : val;
}
function validateSqlAST(topNode) {
(0, _assert.default)(topNode.sqlJoin == null, 'root level field can not have "sqlJoin"');
}
function getConfigFromSchemaObject(fieldOrType) {
var _ref;
return ((_ref = fieldOrType) != null ? (_ref = _ref.extensions) != null ? _ref.joinMonster : _ref : _ref) || {};
}
function objToCursor(obj) {
const str = JSON.stringify(obj);
return Buffer.from(str).toString('base64');
}
function cursorToObj(cursor) {
const str = Buffer.from(cursor, 'base64').toString();
return JSON.parse(str);
}
function maybeQuote(value, dialectName) {
if (value == null) {
return 'NULL';
}
if (typeof value === 'number' || typeof value === 'bigint') return value;
if (value && typeof value.toSQL === 'function') return value.toSQL();
if (value instanceof Buffer && typeof value === 'object' && typeof value.toString === 'function') {
return `X'${value.toString('hex')}'`;
}
if (dialectName === 'oracle' && value.match(/\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(.\d+)?Z?/)) {
return value.replace(/(\d{4}-\d\d-\d\d)T(\d\d:\d\d:\d\d)(.\d+)?Z?/, "TIMESTAMP '$1 $2$3 UTC'");
}
let hasBackslash = false;
let escaped = "'";
for (let i = 0; i < value.length; i++) {
let c = value[i];
if (c === "'") {
escaped += c + c;
} else if (c === '\\') {
escaped += c + c;
hasBackslash = true;
} else {
escaped += c;
}
}
escaped += "'";
if (hasBackslash === true) {
escaped = ' E' + escaped;
}
return escaped;
}
function getDialectName(options) {
if (options.dialectModule) {
return options.dialectModule.name;
}
return options.dialect || 'sqlite3';
}
function buildWhereFunction(type, condition, options) {
const name = getDialectName(options);
if (typeof condition === 'function') {
return condition;
}
const quote = ['mysql', 'mysql8', 'mariadb'].includes(name) ? '`' : '"';
const uniqueKey = getConfigFromSchemaObject(type).uniqueKey;
if (Array.isArray(uniqueKey)) {
_assert.default.equal(condition.length, uniqueKey.length, `The unique key for the "${type.name}" type is a composite. You must provide an array of values for each column.`);
return table => uniqueKey.map((key, i) => `${table}.${quote}${key}${quote} = ${maybeQuote(condition[i])}`).join(' AND ');
}
return table => `${table}.${quote}${uniqueKey}${quote} = ${maybeQuote(condition)}`;
}
function handleUserDbCall(dbCall, sql, sqlAST, shapeDefinition) {
if (dbCall.length === 2) {
return new Promise((resolve, reject) => {
dbCall(sql, (err, rows) => {
if (err) {
reject(err);
} else {
rows = validate(rows);
if (debug.enabled) {
debug(emphasize('RAW_DATA'), inspect(rows.slice(0, 8)));
debug(`${rows.length} rows...`);
}
const data = (0, _nesthydrationjs.nest)(rows, shapeDefinition);
(0, _resolveUnions.default)(data, sqlAST);
if (debug.enabled) {
debug(emphasize('SHAPED_DATA', inspect(data)));
}
resolve(data);
}
});
});
}
const result = dbCall(sql);
if (typeof result.then === 'function') {
return result.then(rows => {
rows = validate(rows);
if (debug.enabled) {
debug(emphasize('RAW DATA'), inspect(rows.slice(0, 8)));
debug(`${rows.length} rows...`);
}
const data = (0, _nesthydrationjs.nest)(rows, shapeDefinition);
(0, _resolveUnions.default)(data, sqlAST);
if (debug.enabled) {
debug(emphasize('SHAPED_DATA'), inspect(data));
}
return data;
});
}
throw new Error('must return a promise of the data or use the callback');
}
function validate(rows) {
if (Array.isArray(rows)) return rows;
if (rows && rows.rows) return rows.rows;
throw new Error(`"dbCall" function must return/resolve an array of objects where each object is a row from the result set.
Instead got ${_util.default.inspect(rows, {
depth: 3
})}`);
}
async function compileSqlAST(sqlAST, context, options) {
if (debug.enabled) {
debug(emphasize('SQL_AST'), inspect(sqlAST));
}
options.dialect = options.dialect || 'sqlite3';
if (options.dialect === 'standard') {
(0, _deprecate.default)('dialect "standard" is deprecated, because there is no true implementation of the SQL standard', '"sqlite3" is the default');
options.dialect = 'sqlite3';
}
const sql = await (0, _dispatcher.default)(sqlAST, context, options);
if (debug.enabled) {
debug(emphasize('SQL'), sql);
}
const shapeDefinition = (0, _defineObjectShape.default)(sqlAST);
if (debug.enabled) {
debug(emphasize('SHAPE_DEFINITION'), inspect(shapeDefinition));
}
return {
sql,
shapeDefinition
};
}