ll-callmobile-backend
Version:
VoIP Mobile Communications Backend with Supabase, Drizzle ORM, and Dynamic Querying - Deployable as Cloudflare Worker
363 lines • 14.1 kB
JavaScript
;
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