Skip to main content
Glama
IBM
by IBM
sqlTemplateProcessor.ts8.53 kB
/** * @fileoverview Shared SQL template processor for consistent template handling * Eliminates duplication between YamlSqlExecutor and SqlLoader * * @deprecated This module is deprecated and no longer actively used. * Template functionality has been consolidated into the unified ParameterProcessor. * This file is kept for reference but should not be used in new code. * * @module src/utils/sql/sqlTemplateProcessor */ import { logger } from "../../../utils/internal/logger.js"; import { RequestContext, requestContextService, } from "../../../utils/internal/requestContext.js"; import { ErrorHandler } from "../../../utils/internal/errorHandler.js"; import { JsonRpcErrorCode } from "../../../types-global/errors.js"; /** * Template processing options */ export interface TemplateProcessingOptions { /** Whether to include detailed logging */ detailedLogging?: boolean; /** Whether to validate template syntax */ validateSyntax?: boolean; /** Custom context for logging */ context?: RequestContext; } /** * Template processing result */ export interface TemplateProcessingResult { /** Processed SQL string */ sql: string; /** Parameters that were used */ usedParameters: string[]; /** Parameters that were missing */ missingParameters: string[]; /** Processing statistics */ stats: { originalLength: number; processedLength: number; ifBlocksProcessed: number; parametersSubstituted: number; }; } /** * Shared SQL template processor * Provides consistent template processing across the application * * @deprecated This class is deprecated and no longer actively used. * Use the unified ParameterProcessor instead for all parameter processing needs. */ export class SqlTemplateProcessor { /** * Process SQL template with parameter substitution * @param template - SQL template string * @param parameters - Parameters for substitution * @param options - Processing options * @returns Processed SQL string */ static async process( template: string, parameters: Record<string, unknown>, options: TemplateProcessingOptions = {}, ): Promise<string> { const result = await this.processWithDetails(template, parameters, options); return result.sql; } /** * Process SQL template with detailed result information * @param template - SQL template string * @param parameters - Parameters for substitution * @param options - Processing options * @returns Detailed processing result */ static async processWithDetails( template: string, parameters: Record<string, unknown>, options: TemplateProcessingOptions = {}, ): Promise<TemplateProcessingResult> { const context = options.context || requestContextService.createRequestContext({ operation: "ProcessSqlTemplate", }); const { detailedLogging = false, validateSyntax = true } = options; return ErrorHandler.tryCatch( async () => { // Validate template syntax if requested if (validateSyntax) { this.validateTemplateSyntax(template); } const usedParameters: string[] = []; const missingParameters: string[] = []; let ifBlocksProcessed = 0; let parametersSubstituted = 0; // Process {{#if condition}} blocks let processedSql = template.replace( /\{\{#if\s+(\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (_match, condition, content) => { ifBlocksProcessed++; const conditionValue = parameters[condition]; if (conditionValue !== undefined) { usedParameters.push(condition); } const isTruthy = Boolean(conditionValue); return isTruthy ? content : ""; }, ); // Process simple parameter substitution {{parameter}} processedSql = processedSql.replace( /\{\{(\w+)\}\}/g, (_match, paramName) => { const paramValue = parameters[paramName]; if (paramValue !== undefined && paramValue !== null) { usedParameters.push(paramName); parametersSubstituted++; return String(paramValue); } missingParameters.push(paramName); logger.warning( { ...context, availableParams: Object.keys(parameters), }, `Parameter '${paramName}' not found in template substitution`, ); return _match; // Return original if parameter not found }, ); // Remove extra whitespace and clean up processedSql = processedSql .replace(/\n\s*\n/g, "\n") // Remove empty lines .trim(); const stats = { originalLength: template.length, processedLength: processedSql.length, ifBlocksProcessed, parametersSubstituted, }; // Log processing details if (detailedLogging) { logger.debug({ ...context, ...stats, usedParameters, missingParameters: missingParameters.length > 0 ? missingParameters : undefined, finalSql: processedSql.length < 200 ? processedSql : processedSql.substring(0, 200) + "...", }); } else { logger.debug({ ...context, originalLength: stats.originalLength, processedLength: stats.processedLength, paramsUsed: usedParameters, }); } return { sql: processedSql, usedParameters: [...new Set(usedParameters)], // Remove duplicates missingParameters: [...new Set(missingParameters)], stats, }; }, { operation: "ProcessSqlTemplate", context, errorCode: JsonRpcErrorCode.ValidationError, }, ); } /** * Validate template syntax * @param template - Template to validate * @private */ private static validateTemplateSyntax(template: string): void { // Check for balanced {{#if}} and {{/if}} blocks const ifBlocks = template.match(/\{\{#if\s+\w+\}\}/g) || []; const endIfBlocks = template.match(/\{\{\/if\}\}/g) || []; if (ifBlocks.length !== endIfBlocks.length) { throw new Error( `Unbalanced {{#if}} blocks: found ${ifBlocks.length} opening and ${endIfBlocks.length} closing blocks`, ); } // Check for invalid parameter syntax - look for unclosed braces or malformed patterns // Look for {{ without matching }} (unclosed opening) const unclosedOpening = template.match(/\{\{[^}]*$/gm) || []; // Look for {{ containing { (nested braces) const nestedBraces = template.match(/\{\{[^}]*\{[^}]*\}\}/g) || []; if (unclosedOpening.length > 0 || nestedBraces.length > 0) { const allInvalid = [...unclosedOpening, ...nestedBraces]; throw new Error( `Invalid parameter syntax found: ${allInvalid.join(", ")}`, ); } } /** * Extract parameter names from template * @param template - Template to analyze * @returns Array of parameter names */ static extractParameterNames(template: string): string[] { const parameters: string[] = []; // Extract from {{#if}} blocks const ifMatches = template.match(/\{\{#if\s+(\w+)\}\}/g) || []; ifMatches.forEach((match) => { const paramName = match.match(/\{\{#if\s+(\w+)\}\}/)?.[1]; if (paramName) { parameters.push(paramName); } }); // Extract from {{parameter}} substitutions const paramMatches = template.match(/\{\{(\w+)\}\}/g) || []; paramMatches.forEach((match) => { const paramName = match.match(/\{\{(\w+)\}\}/)?.[1]; if (paramName) { parameters.push(paramName); } }); return [...new Set(parameters)]; // Remove duplicates } /** * Check if template has conditional blocks * @param template - Template to check * @returns True if template contains {{#if}} blocks */ static hasConditionalBlocks(template: string): boolean { return /\{\{#if\s+\w+\}\}/.test(template); } /** * Check if template has parameter substitutions * @param template - Template to check * @returns True if template contains {{parameter}} substitutions */ static hasParameterSubstitutions(template: string): boolean { return /\{\{\w+\}\}/.test(template); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/IBM/ibmi-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server