UNPKG

slonik-trpc

Version:
259 lines (258 loc) 14.1 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildView = void 0; const slonik_1 = require("slonik"); const sqlUtils_1 = require("../helpers/sqlUtils"); const zod_1 = require("../helpers/zod"); const buildView = (parts, ...values) => { var _a, _b; const context = {}; const fromFragment = slonik_1.sql.fragment(parts, ...values.map(value => { var _a; return typeof value === 'function' ? (_a = value(context)) !== null && _a !== void 0 ? _a : null : value; })); if (!fromFragment.sql.match(/^\s*FROM/i)) { throw new Error("First part of view must be FROM"); } let constraints = null; const options = {}; const allColumns = {}; const preprocessors = []; const config = { table: (_a = fromFragment.sql.match(/^FROM\s*(\w+)/i)) === null || _a === void 0 ? void 0 : _a[1], aliases: new Map(), }; const identifierProxy = new Proxy({}, { get(target, property) { if (property === "_main") return slonik_1.sql.identifier([ config.aliases.get(property) || config.table || "", ]); return slonik_1.sql.identifier([ config.aliases.get(property) || property, ]); }, }); if (!config.table) { config.table = (_b = fromFragment.sql.match(/(AS|\))\s+(\w+)\s*$/i)) === null || _b === void 0 ? void 0 : _b[2]; } const interpreters = {}; const getWhereConditions = (filters, ctx, opts) => __awaiter(void 0, void 0, void 0, function* () { const realContext = Object.assign(Object.assign({}, context), ctx); const realOptions = Object.assign(Object.assign({}, options), opts); const postprocessedFilters = yield preprocessors.slice(-1).reduce((acc, preprocessor) => __awaiter(void 0, void 0, void 0, function* () { const filters = yield acc; return preprocessor(filters, realContext); }), filters); const authConditions = constraints && !(realOptions === null || realOptions === void 0 ? void 0 : realOptions.bypassConstraints) ? yield constraints(realContext) : null; const auth = Array.isArray(authConditions) ? authConditions : [authConditions].filter(zod_1.notEmpty); const conditions = yield interpretFilter(postprocessedFilters || filters, interpreters, realContext, realOptions); return [ ...auth, ...conditions, ]; }); const getWhereFragment = (filters, context, options) => __awaiter(void 0, void 0, void 0, function* () { const conditions = yield getWhereConditions(filters, context, options); return conditions.length ? slonik_1.sql.fragment `WHERE \n(${slonik_1.sql.join(conditions, slonik_1.sql.fragment `)\nAND (`)})\n` : slonik_1.sql.fragment ``; }); const getFromFragment = (ctx = {}) => { return slonik_1.sql.fragment(parts, ...values.map(value => { var _a; return typeof value === 'function' ? (_a = value(ctx)) !== null && _a !== void 0 ? _a : null : value; })); }; const addFilter = (interpreter, fields, mapper, ...otherArgs) => { if (mapper && Array.isArray(fields) && fields.length > 1) { throw new Error("If you specify a mapper function you cannot have multiple filter keys"); } return self.addFilters((Array.isArray(fields) ? fields : [fields]).reduce((acc, key) => { return Object.assign(Object.assign({}, acc), { [key]: (value, allFilters, ctx, key) => { const keys = key.split("."); if (keys.length > 2) { // Ignore middle keys (earlier prefixes), only first and last matter keys.splice(1, keys.length - 2); } const identifier = mapper ? // Try to get the table name from the 2nd to last prefix if it exists, if not then use main table typeof mapper === 'function' ? mapper(identifierProxy, value, allFilters, ctx) : mapper : config.table && keys.length <= 1 ? slonik_1.sql.identifier([ config.table, ...keys.slice(-1), ]) : slonik_1.sql.identifier([...keys.slice(-2)]); return interpreter(value, identifier, ...otherArgs); } }); }, {})); }; const self = Object.assign(Object.assign({}, fromFragment), { getFromFragment, addFilters(filters) { Object.assign(interpreters, filters); return self; }, setTableAliases(newAliases) { for (const [key, value] of Object.entries(newAliases)) { config.aliases.set(key, value); } return self; }, setFilterPreprocess(preprocess) { preprocessors.push(preprocess); return self; }, getFilters(options) { var _a; let prefix = (options === null || options === void 0 ? void 0 : options.table) || ""; if (prefix && !prefix.endsWith(".")) { prefix += "."; } const exclude = ((options === null || options === void 0 ? void 0 : options.exclude) || []); const include = ((options === null || options === void 0 ? void 0 : options.include) || []); const filters = {}; for (const key of Object.keys(interpreters)) { // exclude may have * wildcards that exclude all filters that start with the prefix if (exclude.some((ex) => ex.endsWith("*") ? key.startsWith(ex.replace("*", "")) : ex === key)) { continue; } const isIncluded = !include.length || include.some((ex) => ex.endsWith("*") ? key.startsWith(ex.replace("*", "")) : ex === key); if (isIncluded) { filters[prefix + key.replace(prefix, "")] = { interpret: ((_a = interpreters[key]) === null || _a === void 0 ? void 0 : _a.interpret) || interpreters[key], prefix: key.startsWith(prefix) ? "" : prefix, }; } } return filters; }, addStringFilter: (keys, name) => { return addFilter(sqlUtils_1.stringFilter, keys, name); }, addComparisonFilter: (keys, name, ...otherArgs) => { return addFilter(sqlUtils_1.comparisonFilter, keys, name, ...otherArgs); }, addBooleanFilter: (keys, name, ...otherArgs) => { return addFilter(sqlUtils_1.booleanFilter, keys, name, ...otherArgs); }, addJsonContainsFilter: (keys, name) => { return addFilter(sqlUtils_1.jsonbContainsFilter, keys, name); }, addDateFilter: (keys, name) => { return addFilter(sqlUtils_1.dateFilter, keys, name); }, addInArrayFilter: (keys, name, type) => { const arrFilter = type ? (0, sqlUtils_1.arrayDynamicFilter)(type) : sqlUtils_1.arrayFilter; return addFilter(arrFilter, keys, name); }, addGenericFilter: (name, interpret) => { return addFilter(sqlUtils_1.genericFilter, name, (table, value, ...args) => { return interpret(value, ...args); }); }, options: (opts) => { if (opts && typeof opts === 'object') { for (const [key, value] of Object.entries(opts)) { options[key] = value; } } return self; }, context: (ctx) => { if (ctx && typeof ctx === 'object') { for (const [key, value] of Object.entries(ctx)) { context[key] = value; } } return self; }, setConstraints(cons) { constraints = cons; return self; }, getWhereConditions: (args) => __awaiter(void 0, void 0, void 0, function* () { return getWhereConditions(args.where, args.ctx, args.options); }), load: (args) => __awaiter(void 0, void 0, void 0, function* () { const db = args.db || options.db; if (!db) { throw new Error('Database is not set. Please set the database by calling options({ db: db })'); } if (args.take === 0) return []; const realContext = Object.assign(Object.assign({}, context), args.ctx); const whereFragment = args.where ? yield getWhereFragment(args.where, realContext, options) : slonik_1.sql.fragment ``; const selectFrag = Array.isArray(args.select) ? slonik_1.sql.fragment `SELECT ${slonik_1.sql.join(args.select.map(key => allColumns[key]).filter((frag) => { return frag && (frag.sql || frag.type); }), slonik_1.sql.fragment `\n, `)}` : args.select; const query = slonik_1.sql.unsafe `${selectFrag} ${getFromFragment(realContext)} ${whereFragment} ${args.groupBy ? slonik_1.sql.fragment `GROUP BY ${args.groupBy}` : slonik_1.sql.fragment ``} ${args.orderBy ? slonik_1.sql.fragment `ORDER BY ${args.orderBy}` : slonik_1.sql.fragment ``} ${typeof args.take === 'number' && args.take > 0 ? slonik_1.sql.fragment `LIMIT ${args.take}` : slonik_1.sql.fragment ``} ${typeof args.skip === 'number' && args.skip > 0 ? slonik_1.sql.fragment `OFFSET ${args.skip}` : slonik_1.sql.fragment ``} `; return db.any(query); }), setColumns: (columns) => { if (Array.isArray(columns)) { for (const column of columns) { if (typeof column === 'string') { allColumns[column] = slonik_1.sql.identifier([column]); } } } else if (typeof columns === 'object') { Object.keys(columns).forEach(key => columns[key] && (allColumns[key] = columns[key])); } return self; }, getWhereFragment: (args) => __awaiter(void 0, void 0, void 0, function* () { return getWhereFragment(args.where, args.ctx, args.options); }) }); return self; }; exports.buildView = buildView; const interpretFilter = (filter, interpreters, context, options) => __awaiter(void 0, void 0, void 0, function* () { var _a, _b, _c; const conditions = []; const addCondition = (item) => item && conditions.push(item); for (const key of Object.keys(filter)) { const interpreter = interpreters[key]; const condition = yield ((_a = ((interpreter === null || interpreter === void 0 ? void 0 : interpreter.interpret) || interpreter)) === null || _a === void 0 ? void 0 : _a(filter[key], filter, context, key)); if (condition) { addCondition(condition); } } if ((_b = filter.OR) === null || _b === void 0 ? void 0 : _b.length) { if (!(options === null || options === void 0 ? void 0 : options.orEnabled)) { throw new Error("OR filters are not enabled. Please enable by passing { orFilterEnabled: true } in the options"); } const orConditions = yield Promise.all(filter.OR.map((or) => __awaiter(void 0, void 0, void 0, function* () { const orFilter = yield interpretFilter(or, interpreters, context, options); return (orFilter === null || orFilter === void 0 ? void 0 : orFilter.length) ? slonik_1.sql.fragment `(${slonik_1.sql.join(orFilter, slonik_1.sql.fragment `) AND (`)})` : null; }))).then((filters) => filters.filter(zod_1.notEmpty)); if (orConditions === null || orConditions === void 0 ? void 0 : orConditions.length) { addCondition(slonik_1.sql.fragment `(${slonik_1.sql.join(orConditions, slonik_1.sql.fragment `)\n OR (`)})`); } } if ((_c = filter.AND) === null || _c === void 0 ? void 0 : _c.length) { const andConditions = yield Promise.all(filter.AND.map((and) => __awaiter(void 0, void 0, void 0, function* () { const andFilter = yield interpretFilter(and, interpreters, context, options); return (andFilter === null || andFilter === void 0 ? void 0 : andFilter.length) ? slonik_1.sql.fragment `(${slonik_1.sql.join(andFilter, slonik_1.sql.fragment `) AND (`)})` : null; }))).then((filters) => filters.filter(zod_1.notEmpty)); if (andConditions === null || andConditions === void 0 ? void 0 : andConditions.length) { addCondition(slonik_1.sql.fragment `(${slonik_1.sql.join(andConditions, slonik_1.sql.fragment `)\n AND (`)})`); } } if (filter.NOT) { const notFilter = yield interpretFilter(filter.NOT, interpreters, context, options); if (notFilter.length) { addCondition(slonik_1.sql.fragment `NOT (${slonik_1.sql.join(notFilter, slonik_1.sql.fragment `) AND (`)})`); } } return conditions; });