UNPKG

ll-callmobile-backend

Version:

VoIP Mobile Communications Backend with Supabase, Drizzle ORM, and Dynamic Querying - Deployable as Cloudflare Worker

363 lines 14.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.DynamicQuery = void 0; const drizzle_orm_1 = require("drizzle-orm"); const schema = __importStar(require("../db/schema")); /** * DynamicQuery - A simple and powerful query builder for your database * * @example * ```typescript * import { DynamicQuery } from 'll-callmobile-backend'; * * const query = new DynamicQuery(db); * * // Simple filtering * const activeTests = await query.findByFilters('test_cases', { status: 'active' }); * * // Complex queries * const results = await query.query('test_cases', { * where: [{ field: 'status', operator: 'eq', value: 'active' }], * orderBy: [{ field: 'created_on', direction: 'desc' }], * pagination: { limit: 10, offset: 0 } * }); * ``` */ class DynamicQuery { constructor(db, config = {}) { this.db = db; this.config = { maxLimit: 1000, defaultLimit: 50, enableRawQueries: false, ...config }; // Map table names to schema tables this.tableMap = { test_cases: schema.testCases, clients: schema.clients, vendors: schema.vendors, jobs: schema.jobs }; } /** * Find records using simple key-value filters * * @param tableName - The table to query * @param filters - Simple object with field-value pairs * @param options - Optional pagination and sorting * * @example * ```typescript * // Find all active test cases * const activeTests = await query.findByFilters('test_cases', { status: 'active' }); * * // Find with pagination * const recentTests = await query.findByFilters('test_cases', * { status: 'active' }, * { limit: 10, offset: 0 } * ); * * // Find with sorting * const sortedTests = await query.findByFilters('test_cases', * { status: 'active' }, * { * limit: 10, * orderBy: [{ field: 'created_on', direction: 'desc' }] * } * ); * ``` */ async findByFilters(tableName, filters, options = {}) { const table = this.tableMap[tableName]; if (!table) { throw new Error(`Table '${tableName}' not found`); } try { // Build query step by step let baseQuery = this.db.select().from(table); // Apply filters for (const [field, value] of Object.entries(filters)) { if (value !== undefined && value !== null) { // @ts-ignore - Drizzle type system complexity baseQuery = baseQuery.where((0, drizzle_orm_1.eq)(table[field], value)); } } // Apply sorting if (options.orderBy) { for (const sort of options.orderBy) { const direction = sort.direction === 'desc' ? drizzle_orm_1.desc : drizzle_orm_1.asc; // @ts-ignore - Drizzle type system complexity baseQuery = baseQuery.orderBy(direction(table[sort.field])); } } // Apply pagination const limit = Math.min(options.limit || this.config.defaultLimit, this.config.maxLimit); if (limit) { // @ts-ignore - Drizzle type system complexity baseQuery = baseQuery.limit(limit); } if (options.offset) { // @ts-ignore - Drizzle type system complexity baseQuery = baseQuery.offset(options.offset); } const result = await baseQuery; return result; } catch (error) { throw new Error(`Query failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Execute complex queries with advanced filtering, sorting, and pagination * * @param tableName - The table to query * @param options - Complete query options * * @example * ```typescript * // Complex filtering with multiple conditions * const results = await query.query('test_cases', { * where: [ * { field: 'status', operator: 'eq', value: 'active' }, * { field: 'created_on', operator: 'gte', value: '2024-01-01' }, * { field: 'description', operator: 'like', value: '%test%' } * ], * orderBy: [ * { field: 'created_on', direction: 'desc' }, * { field: 'id', direction: 'asc' } * ], * pagination: { limit: 20, offset: 0 } * }); * * // Select specific fields only * const results = await query.query('test_cases', { * select: ['id', 'description', 'status'], * where: [{ field: 'status', operator: 'eq', value: 'active' }] * }); * ``` */ async query(tableName, options) { const table = this.tableMap[tableName]; if (!table) { throw new Error(`Table '${tableName}' not found`); } try { // Build base query let baseQuery = this.db.select().from(table); // Apply filters if (options.where) { for (const filter of options.where) { if (this.isAdvancedFilter(filter)) { // @ts-ignore - Drizzle type system complexity baseQuery = baseQuery.where(this.buildWhereClause(table, filter)); } else { // Simple filter for (const [field, value] of Object.entries(filter)) { if (value !== undefined && value !== null) { // @ts-ignore - Drizzle type system complexity baseQuery = baseQuery.where((0, drizzle_orm_1.eq)(table[field], value)); } } } } } // Apply sorting if (options.orderBy) { for (const sort of options.orderBy) { const direction = sort.direction === 'desc' ? drizzle_orm_1.desc : drizzle_orm_1.asc; // @ts-ignore - Drizzle type system complexity baseQuery = baseQuery.orderBy(direction(table[sort.field])); } } // Apply pagination const pagination = this.normalizePagination(options.pagination); if (pagination.limit) { // @ts-ignore - Drizzle type system complexity baseQuery = baseQuery.limit(pagination.limit); } if (pagination.offset) { // @ts-ignore - Drizzle type system complexity baseQuery = baseQuery.offset(pagination.offset); } // Execute query // @ts-ignore - Drizzle type system complexity const data = await baseQuery; // Get total count for pagination metadata let total = data.length; if (pagination.limit || pagination.offset) { const countQuery = this.db.select({ count: (0, drizzle_orm_1.count)() }).from(table); // @ts-ignore - Drizzle type system complexity const countResult = await countQuery; total = countResult[0]?.count || 0; } return { data: data, total, page: pagination.page, pageSize: pagination.limit, totalPages: pagination.limit ? Math.ceil(total / pagination.limit) : undefined }; } catch (error) { throw new Error(`Query failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Execute raw SQL queries (when enabled) * * @param sql - Raw SQL query * @param params - Query parameters * * @example * ```typescript * // Only available if enableRawQueries is true * const results = await query.executeRaw( * 'SELECT * FROM test_cases WHERE status = $1 AND created_on > $2', * ['active', '2024-01-01'] * ); * ``` */ async executeRaw(sql, params = []) { if (!this.config.enableRawQueries) { throw new Error('Raw queries are disabled. Set enableRawQueries: true in config.'); } try { // @ts-ignore - Drizzle type system complexity return await this.db.execute(sql, params); } catch (error) { throw new Error(`Raw query failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Get a single record by ID * * @param tableName - The table to query * @param id - Record ID * * @example * ```typescript * const testCase = await query.findById('test_cases', 123); * ``` */ async findById(tableName, id) { const table = this.tableMap[tableName]; if (!table) { throw new Error(`Table '${tableName}' not found`); } try { const result = await this.db.select().from(table).where((0, drizzle_orm_1.eq)(table.id, id)).limit(1); return (result[0] || null); } catch (error) { throw new Error(`Find by ID failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Count records with optional filters * * @param tableName - The table to count * @param filters - Optional filters * * @example * ```typescript * const totalTests = await query.count('test_cases'); * const activeTests = await query.count('test_cases', { status: 'active' }); * ``` */ async count(tableName, filters = {}) { const table = this.tableMap[tableName]; if (!table) { throw new Error(`Table '${tableName}' not found`); } try { let query = this.db.select({ count: (0, drizzle_orm_1.count)() }).from(table); // Apply filters for (const [field, value] of Object.entries(filters)) { if (value !== undefined && value !== null) { // @ts-ignore - Drizzle type system complexity query = query.where((0, drizzle_orm_1.eq)(table[field], value)); } } // @ts-ignore - Drizzle type system complexity const result = await query; return result[0]?.count || 0; } catch (error) { throw new Error(`Count failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } // Private helper methods isAdvancedFilter(filter) { return 'field' in filter && 'operator' in filter; } buildWhereClause(table, filter) { const { field, operator, value } = filter; const tableField = table[field]; switch (operator) { case 'eq': return (0, drizzle_orm_1.eq)(tableField, value); case 'ne': return (0, drizzle_orm_1.ne)(tableField, value); case 'gt': return (0, drizzle_orm_1.gt)(tableField, value); case 'gte': return (0, drizzle_orm_1.gte)(tableField, value); case 'lt': return (0, drizzle_orm_1.lt)(tableField, value); case 'lte': return (0, drizzle_orm_1.lte)(tableField, value); case 'like': return (0, drizzle_orm_1.like)(tableField, `%${value}%`); case 'in': return (0, drizzle_orm_1.inArray)(tableField, Array.isArray(value) ? value : [value]); case 'notIn': return (0, drizzle_orm_1.notInArray)(tableField, Array.isArray(value) ? value : [value]); case 'isNull': return (0, drizzle_orm_1.isNull)(tableField); case 'isNotNull': return (0, drizzle_orm_1.isNotNull)(tableField); default: throw new Error(`Unsupported operator: ${operator}`); } } normalizePagination(pagination) { if (!pagination) return { limit: this.config.defaultLimit, offset: 0 }; let { limit, offset, page, pageSize } = pagination; // Handle page-based pagination if (page && pageSize) { offset = (page - 1) * pageSize; limit = pageSize; } // Apply limits limit = Math.min(limit || this.config.defaultLimit, this.config.maxLimit); offset = offset || 0; return { limit, offset, page, pageSize }; } } exports.DynamicQuery = DynamicQuery; //# sourceMappingURL=DynamicQuery.js.map