UNPKG

join-monster

Version:

A GraphQL to SQL query execution layer for batch data fetching.

194 lines (193 loc) 6.7 kB
"use strict"; 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 }; }